1ff149285aa2d2c297cf48b2ead4c708d8b23852
[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
73
74
75
76
77 int BC_WindowBase::shm_completion_event = -1;
78
79
80
81 BC_Resources BC_WindowBase::resources;
82
83 Window XGroupLeader = 0;
84
85 Mutex BC_KeyboardHandlerLock::keyboard_listener_mutex("keyboard_listener",0);
86 ArrayList<BC_KeyboardHandler*> BC_KeyboardHandler::listeners;
87
88 BC_WindowBase::BC_WindowBase()
89 {
90 //printf("BC_WindowBase::BC_WindowBase 1\n");
91         BC_WindowBase::initialize();
92 }
93
94 BC_WindowBase::~BC_WindowBase()
95 {
96 #ifdef SINGLE_THREAD
97         BC_Display::lock_display("BC_WindowBase::~BC_WindowBase");
98 #else
99         if(window_type == MAIN_WINDOW)
100                 lock_window("BC_WindowBase::~BC_WindowBase");
101 #endif
102
103 #ifdef HAVE_LIBXXF86VM
104         if(window_type == VIDMODE_SCALED_WINDOW && vm_switched) {
105                 restore_vm();
106         }
107 #endif
108         is_deleting = 1;
109
110         hide_tooltip();
111         if(window_type != MAIN_WINDOW)
112         {
113 // stop event input
114                 XSelectInput(top_level->display, this->win, 0);
115                 XSync(top_level->display,0);
116 #ifndef SINGLE_THREAD
117                 top_level->dequeue_events(win);
118 #endif
119 // drop active window refs to this
120                 if(top_level->active_menubar == this) top_level->active_menubar = 0;
121                 if(top_level->active_popup_menu == this) top_level->active_popup_menu = 0;
122                 if(top_level->active_subwindow == this) top_level->active_subwindow = 0;
123 // drop motion window refs to this
124                 if(top_level->motion_events && top_level->last_motion_win == this->win)
125                         top_level->motion_events = 0;
126
127 // Remove pointer from parent window to this
128                 parent_window->subwindows->remove(this);
129         }
130
131         if(grab_active) grab_active->active_grab = 0;
132         if(icon_window) delete icon_window;
133         if(window_type == POPUP_WINDOW)
134                 parent_window->remove_popup(this);
135
136 // Delete the subwindows
137         if(subwindows)
138         {
139                 while(subwindows->total)
140                 {
141 // Subwindow removes its own pointer
142                         delete subwindows->values[0];
143                 }
144                 delete subwindows;
145         }
146
147         delete pixmap;
148
149 //printf("delete glx=%08x, win=%08x %s\n", (unsigned)glx_win, (unsigned)win, title);
150 #ifdef HAVE_GL
151         if( get_resources()->get_synchronous() && glx_win != 0 ) {
152                 if( window_type == MAIN_WINDOW )
153                         unlock_window();
154                 get_resources()->get_synchronous()->delete_window(this);
155                 if( window_type == MAIN_WINDOW )
156                         lock_window("BC_WindowBase::delete_window");
157         }
158 #endif
159         XDestroyWindow(top_level->display, win);
160
161         if(bg_pixmap && !shared_bg_pixmap) delete bg_pixmap;
162         if(icon_pixmap) delete icon_pixmap;
163         if(temp_bitmap) delete temp_bitmap;
164         top_level->active_bitmaps.remove_buffers(this);
165         if(_7segment_pixmaps)
166         {
167                 for(int i = 0; i < TOTAL_7SEGMENT; i++)
168                         delete _7segment_pixmaps[i];
169
170                 delete [] _7segment_pixmaps;
171         }
172
173
174
175         if(window_type == MAIN_WINDOW)
176         {
177                 XFreeGC(display, gc);
178                 static XFontStruct *BC_WindowBase::*xfont[] = {
179                          &BC_WindowBase::smallfont,
180                          &BC_WindowBase::mediumfont,
181                          &BC_WindowBase::largefont,
182                          &BC_WindowBase::bigfont,
183                          &BC_WindowBase::clockfont,
184                 };
185                 for( int i=sizeof(xfont)/sizeof(xfont[0]); --i>=0; )
186                         XFreeFont(display, this->*xfont[i]);
187
188 #ifdef HAVE_XFT
189 // prevents a bug when Xft closes with unrefd fonts
190                 FcPattern *defaults = FcPatternCreate();
191                 FcPatternAddInteger(defaults, "maxunreffonts", 0);
192                 XftDefaultSet(display, defaults);
193
194                 static void *BC_WindowBase::*xft_font[] = {
195                          &BC_WindowBase::smallfont_xft,
196                          &BC_WindowBase::mediumfont_xft,
197                          &BC_WindowBase::largefont_xft,
198                          &BC_WindowBase::bigfont_xft,
199                          &BC_WindowBase::bold_smallfont_xft,
200                          &BC_WindowBase::bold_mediumfont_xft,
201                          &BC_WindowBase::bold_largefont_xft,
202                          &BC_WindowBase::clockfont_xft,
203                 };
204                 for( int i=sizeof(xft_font)/sizeof(xft_font[0]); --i>=0; ) {
205                         XftFont *xft = (XftFont *)(this->*xft_font[i]);
206                         if( xft ) xftFontClose (display, xft);
207                 }
208 #endif
209                 finit_im();
210                 flush();
211                 sync_display();
212
213                 if( xinerama_info )
214                         XFree(xinerama_info);
215                 xinerama_screens = 0;
216                 xinerama_info = 0;
217                 if( xvideo_port_id >= 0 )
218                         XvUngrabPort(display, xvideo_port_id, CurrentTime);
219
220                 unlock_window();
221 // Must be last reference to display.
222 // _XftDisplayInfo needs a lock.
223                 get_resources()->create_window_lock->lock("BC_WindowBase::~BC_WindowBase");
224                 XCloseDisplay(display);
225                 get_resources()->create_window_lock->unlock();
226
227 // clipboard uses a different display connection
228                 clipboard->stop_clipboard();
229                 delete clipboard;
230         }
231
232         resize_history.remove_all_objects();
233
234 #ifndef SINGLE_THREAD
235         common_events.remove_all_objects();
236         delete event_lock;
237         delete event_condition;
238         delete init_lock;
239 #else
240         top_level->window_lock = 0;
241         BC_Display::unlock_display();
242 #endif
243         delete cursor_timer;
244
245 #if HAVE_GL
246         if( glx_fbcfgs_window ) XFree(glx_fbcfgs_window);
247         if( glx_fbcfgs_pbuffer) XFree(glx_fbcfgs_pbuffer);
248         if( glx_fbcfgs_pixmap ) XFree(glx_fbcfgs_pixmap);
249 #endif
250
251         UNSET_ALL_LOCKS(this)
252 }
253
254 int BC_WindowBase::initialize()
255 {
256         done = 0;
257         done_set = 0;
258         window_running = 0;
259         display_lock_owner = 0;
260         test_keypress = 0;
261         keys_return[0] = 0;
262         is_deleting = 0;
263         window_lock = 0;
264         resend_event_window = 0;
265         x = 0;
266         y = 0;
267         w = 0;
268         h = 0;
269         bg_color = -1;
270         line_width = 1;
271         line_dashes = 0;
272         top_level = 0;
273         parent_window = 0;
274         subwindows = 0;
275         xinerama_info = 0;
276         xinerama_screens = 0;
277         xvideo_port_id = -1;
278         video_on = 0;
279         motion_events = 0;
280         resize_events = 0;
281         translation_events = 0;
282         ctrl_mask = shift_mask = alt_mask = 0;
283         cursor_x = cursor_y = button_number = 0;
284         button_down = 0;
285         button_pressed = 0;
286         button_time1 = 0;
287         button_time2 = 0;
288         button_time3 = 0;
289         double_click = 0;
290         triple_click = 0;
291         event_win = 0;
292         last_motion_win = 0;
293         key_pressed = 0;
294         active_grab = 0;
295         grab_active = 0;
296         active_menubar = 0;
297         active_popup_menu = 0;
298         active_subwindow = 0;
299         cursor_entered = 0;
300         pixmap = 0;
301         bg_pixmap = 0;
302         _7segment_pixmaps = 0;
303         tooltip_text = 0;
304         force_tooltip = 0;
305 //      next_repeat_id = 0;
306         tooltip_popup = 0;
307         current_font = MEDIUMFONT;
308         current_color = BLACK;
309         current_cursor = ARROW_CURSOR;
310         hourglass_total = 0;
311         is_dragging = 0;
312         shared_bg_pixmap = 0;
313         icon_pixmap = 0;
314         icon_window = 0;
315         window_type = MAIN_WINDOW;
316         translation_count = 0;
317         x_correction = y_correction = 0;
318         temp_bitmap = 0;
319         tooltip_on = 0;
320         temp_cursor = 0;
321         toggle_value = 0;
322         toggle_drag = 0;
323         has_focus = 0;
324         is_hourglass = 0;
325         is_transparent = 0;
326 #ifdef HAVE_LIBXXF86VM
327         vm_switched = 0;
328 #endif
329         input_method = 0;
330         input_context = 0;
331
332         smallfont = 0;
333         mediumfont = 0;
334         largefont = 0;
335         bigfont = 0;
336         clockfont = 0;
337
338         smallfont_xft = 0;
339         mediumfont_xft = 0;
340         largefont_xft = 0;
341         bigfont_xft = 0;
342         clockfont_xft = 0;
343
344         bold_smallfont_xft = 0;
345         bold_mediumfont_xft = 0;
346         bold_largefont_xft = 0;
347 #ifdef SINGLE_THREAD
348         completion_lock = new Condition(0, "BC_WindowBase::completion_lock");
349 #else
350 // Need these right away since put_event is called before run_window sometimes.
351         event_lock = new Mutex("BC_WindowBase::event_lock");
352         event_condition = new Condition(0, "BC_WindowBase::event_condition");
353         init_lock = new Condition(0, "BC_WindowBase::init_lock");
354 #endif
355
356         cursor_timer = new Timer;
357         event_thread = 0;
358 #ifdef HAVE_GL
359         glx_fbcfgs_window = 0;  n_fbcfgs_window = 0;
360         glx_fbcfgs_pbuffer = 0; n_fbcfgs_pbuffer = 0;
361         glx_fbcfgs_pixmap = 0;  n_fbcfgs_pixmap = 0;
362
363         glx_fb_config = 0;
364         glx_win_context = 0;
365         glx_win = 0;
366 #endif
367
368         flash_enabled = 1;
369         win = 0;
370         return 0;
371 }
372
373
374
375 #define DEFAULT_EVENT_MASKS EnterWindowMask | \
376                         LeaveWindowMask | \
377                         ButtonPressMask | \
378                         ButtonReleaseMask | \
379                         PointerMotionMask | \
380                         FocusChangeMask
381
382
383 int BC_WindowBase::create_window(BC_WindowBase *parent_window, const char *title,
384                 int x, int y, int w, int h, int minw, int minh, int allow_resize,
385                 int private_color, int hide, int bg_color, const char *display_name,
386                 int window_type, BC_Pixmap *bg_pixmap, int group_it)
387 {
388         XSetWindowAttributes attr;
389         unsigned long mask;
390         XSizeHints size_hints;
391         int root_w;
392         int root_h;
393 #ifdef HAVE_LIBXXF86VM
394         int vm;
395 #endif
396
397         id = get_resources()->get_id();
398         if(parent_window) top_level = parent_window->top_level;
399         if( top_level ) lock_window("BC_WindowBase::create_window");
400         get_resources()->create_window_lock->lock("BC_WindowBase::create_window");
401
402 #ifdef HAVE_LIBXXF86VM
403         if(window_type == VIDMODE_SCALED_WINDOW)
404                 closest_vm(&vm,&w,&h);
405 #endif
406
407         this->x = x;
408         this->y = y;
409         this->w = w;
410         this->h = h;
411         this->bg_color = bg_color;
412         this->window_type = window_type;
413         this->hidden = hide;
414         this->private_color = private_color;
415         this->parent_window = parent_window;
416         this->bg_pixmap = bg_pixmap;
417         this->allow_resize = allow_resize;
418         if(display_name)
419                 strcpy(this->display_name, display_name);
420         else
421                 this->display_name[0] = 0;
422
423         put_title(title);
424         if(bg_pixmap) shared_bg_pixmap = 1;
425
426         subwindows = new BC_SubWindowList;
427
428         if(window_type == MAIN_WINDOW)
429         {
430                 top_level = this;
431                 parent_window = this;
432
433
434 #ifdef SINGLE_THREAD
435                 display = BC_Display::get_display(display_name);
436                 BC_Display::lock_display("BC_WindowBase::create_window");
437 //              BC_Display::display_global->new_window(this);
438 #else
439
440 // get the display connection
441
442 // This function must be the first Xlib
443 // function a multi-threaded program calls
444                 XInitThreads();
445                 display = init_display(display_name);
446                 if( shm_completion_event < 0 ) shm_completion_event =
447                         ShmCompletion + XShmGetEventBase(display);
448 #endif
449                 lock_window("BC_WindowBase::create_window 1");
450
451                 screen = DefaultScreen(display);
452                 rootwin = RootWindow(display, screen);
453 // window placement boundaries
454                 if( !xinerama_screens && XineramaIsActive(display) )
455                         xinerama_info = XineramaQueryScreens(display, &xinerama_screens);
456                 root_w = get_root_w(0);
457                 root_h = get_root_h(0);
458
459 #if HAVE_GL
460                 vis = get_glx_visual(display);
461                 if( !vis )
462 #endif
463                         vis = DefaultVisual(display, screen);
464
465                 default_depth = DefaultDepth(display, screen);
466
467                 client_byte_order = (*(const u_int32_t*)"a   ") & 0x00000001;
468                 server_byte_order = (XImageByteOrder(display) == MSBFirst) ? 0 : 1;
469
470
471 // This must be done before fonts to know if antialiasing is available.
472                 init_colors();
473 // get the resources
474                 if(resources.use_shm < 0) resources.initialize_display(this);
475                 x_correction = BC_DisplayInfo::get_left_border();
476                 y_correction = BC_DisplayInfo::get_top_border();
477
478 // clamp window placement
479                 if(this->x + this->w + x_correction > root_w)
480                         this->x = root_w - this->w - x_correction;
481                 if(this->y + this->h + y_correction > root_h)
482                         this->y = root_h - this->h - y_correction;
483                 if(this->x < 0) this->x = 0;
484                 if(this->y < 0) this->y = 0;
485
486                 if(this->bg_color == -1)
487                         this->bg_color = resources.get_bg_color();
488
489 // printf("bcwindowbase 1 %s\n", title);
490 // if(window_type == MAIN_WINDOW) sleep(1);
491 // printf("bcwindowbase 10\n");
492                 init_fonts();
493                 init_gc();
494                 init_cursors();
495
496 // Create the window
497                 mask = CWEventMask | CWBackPixel | CWColormap | CWCursor;
498
499                 attr.event_mask = DEFAULT_EVENT_MASKS |
500                         StructureNotifyMask |
501                         KeyPressMask |
502                         KeyReleaseMask;
503
504                 attr.background_pixel = get_color(this->bg_color);
505                 attr.colormap = cmap;
506                 attr.cursor = get_cursor_struct(ARROW_CURSOR);
507
508                 win = XCreateWindow(display, rootwin,
509                         this->x, this->y, this->w, this->h, 0,
510                         top_level->default_depth, InputOutput,
511                         vis, mask, &attr);
512                 XGetNormalHints(display, win, &size_hints);
513
514                 size_hints.flags = PSize | PMinSize | PMaxSize;
515                 size_hints.width = this->w;
516                 size_hints.height = this->h;
517                 size_hints.min_width = allow_resize ? minw : this->w;
518                 size_hints.max_width = allow_resize ? 32767 : this->w;
519                 size_hints.min_height = allow_resize ? minh : this->h;
520                 size_hints.max_height = allow_resize ? 32767 : this->h;
521                 if(x > -BC_INFINITY && x < BC_INFINITY)
522                 {
523                         size_hints.flags |= PPosition;
524                         size_hints.x = this->x;
525                         size_hints.y = this->y;
526                 }
527                 XSetWMProperties(display, win, 0, 0, 0, 0, &size_hints, 0, 0);
528                 get_atoms();
529                 set_title(title);
530 #ifndef SINGLE_THREAD
531                 clipboard = new BC_Clipboard(this);
532                 clipboard->start_clipboard();
533 #endif
534
535
536                 if (group_it)
537                 {
538                         Atom ClientLeaderXAtom;
539                         if (XGroupLeader == 0)
540                                 XGroupLeader = win;
541                         const char *instance_name = "cinelerra";
542                         const char *class_name = "Cinelerra";
543                         XClassHint *class_hints = XAllocClassHint();
544                         class_hints->res_name = (char*)instance_name;
545                         class_hints->res_class = (char*)class_name;
546                         XSetClassHint(top_level->display, win, class_hints);
547                         XFree(class_hints);
548                         ClientLeaderXAtom = XInternAtom(display, "WM_CLIENT_LEADER", True);
549                         XChangeProperty(display, win, ClientLeaderXAtom, XA_WINDOW, 32,
550                                 PropModeReplace, (unsigned char *)&XGroupLeader, true);
551                 }
552                 init_im();
553                 set_icon(get_resources()->default_icon);
554         }
555
556 #ifdef HAVE_LIBXXF86VM
557         if(window_type == VIDMODE_SCALED_WINDOW && vm != -1)
558         {
559                 scale_vm (vm);
560                 vm_switched = 1;
561         }
562 #endif
563
564 #ifdef HAVE_LIBXXF86VM
565         if(window_type == POPUP_WINDOW || window_type == VIDMODE_SCALED_WINDOW)
566 #else
567         if(window_type == POPUP_WINDOW)
568 #endif
569         {
570                 mask = CWEventMask | CWBackPixel | CWColormap |
571                         CWOverrideRedirect | CWSaveUnder | CWCursor;
572
573                 attr.event_mask = DEFAULT_EVENT_MASKS | ExposureMask |
574                         KeyPressMask | KeyReleaseMask;
575
576                 if(this->bg_color == -1)
577                         this->bg_color = resources.get_bg_color();
578                 attr.background_pixel = top_level->get_color(bg_color);
579                 attr.colormap = top_level->cmap;
580                 if(top_level->is_hourglass)
581                         attr.cursor = top_level->get_cursor_struct(HOURGLASS_CURSOR);
582                 else
583                         attr.cursor = top_level->get_cursor_struct(ARROW_CURSOR);
584                 attr.override_redirect = True;
585                 attr.save_under = True;
586
587                 win = XCreateWindow(top_level->display,
588                         top_level->rootwin, this->x, this->y, this->w, this->h, 0,
589                         top_level->default_depth, InputOutput, top_level->vis, mask,
590                         &attr);
591                 top_level->add_popup(this);
592         }
593
594         if(window_type == SUB_WINDOW)
595         {
596                 mask = CWEventMask | CWBackPixel | CWCursor;
597                 attr.event_mask = DEFAULT_EVENT_MASKS;
598                 attr.background_pixel = top_level->get_color(this->bg_color);
599                 if(top_level->is_hourglass)
600                         attr.cursor = top_level->get_cursor_struct(HOURGLASS_CURSOR);
601                 else
602                         attr.cursor = top_level->get_cursor_struct(ARROW_CURSOR);
603                 win = XCreateWindow(top_level->display,
604                         parent_window->win, this->x, this->y, this->w, this->h, 0,
605                         top_level->default_depth, InputOutput, top_level->vis, mask,
606                         &attr);
607                 init_window_shape();
608                 if(!hidden) XMapWindow(top_level->display, win);
609         }
610
611 // Create pixmap for all windows
612         pixmap = new BC_Pixmap(this, this->w, this->h);
613
614 // Set up options for main window
615         if(window_type == MAIN_WINDOW)
616         {
617                 if(get_resources()->bg_image && !bg_pixmap && bg_color < 0)
618                 {
619                         this->bg_pixmap = new BC_Pixmap(this,
620                                 get_resources()->bg_image,
621                                 PIXMAP_OPAQUE);
622                 }
623
624                 if(!hidden) show_window();
625
626         }
627
628         draw_background(0, 0, this->w, this->h);
629
630         flash(-1, -1, -1, -1, 0);
631
632 // Set up options for popup window
633 #ifdef HAVE_LIBXXF86VM
634         if(window_type == POPUP_WINDOW || window_type == VIDMODE_SCALED_WINDOW)
635 #else
636         if(window_type == POPUP_WINDOW)
637 #endif
638         {
639                 init_window_shape();
640                 if(!hidden) show_window();
641         }
642         get_resources()->create_window_lock->unlock();
643         unlock_window();
644
645         return 0;
646 }
647
648 Display* BC_WindowBase::init_display(const char *display_name)
649 {
650         Display* display;
651
652         if(display_name && display_name[0] == 0) display_name = NULL;
653         if((display = XOpenDisplay(display_name)) == NULL) {
654                 printf("BC_WindowBase::init_display: cannot connect to X server %s\n",
655                         display_name);
656                 if(getenv("DISPLAY") == NULL) {
657                         printf(_("'DISPLAY' environment variable not set.\n"));
658                         exit(1);
659                 }
660 // Try again with default display.
661                 if((display = XOpenDisplay(0)) == NULL) {
662                         printf("BC_WindowBase::init_display: cannot connect to default X server.\n");
663                         exit(1);
664                 }
665         }
666
667         static int xsynch = -1;
668         if( xsynch < 0 ) {
669                 const char *cp = getenv("CIN_XSYNCH");
670                 xsynch = !cp ? 0 : atoi(cp);
671         }
672         if( xsynch > 0 )
673                 XSynchronize(display, True);
674
675         return display;
676 }
677
678 Display* BC_WindowBase::get_display()
679 {
680         return top_level->display;
681 }
682
683 int BC_WindowBase::get_screen()
684 {
685         return top_level->screen;
686 }
687
688 int BC_WindowBase::run_window()
689 {
690         done_set = done = 0;
691         return_value = 0;
692
693
694 // Events may have been sent before run_window so can't initialize them here.
695
696 #ifdef SINGLE_THREAD
697         set_repeat(get_resources()->tooltip_delay);
698         BC_Display::display_global->new_window(this);
699
700 // If the first window created, run the display loop in this thread.
701         if(BC_Display::display_global->is_first(this))
702         {
703                 BC_Display::unlock_display();
704                 BC_Display::display_global->loop();
705         }
706         else
707         {
708                 BC_Display::unlock_display();
709                 completion_lock->lock("BC_WindowBase::run_window");
710         }
711
712         BC_Display::lock_display("BC_WindowBase::run_window");
713         BC_Display::display_global->delete_window(this);
714
715         unset_all_repeaters();
716         hide_tooltip();
717         BC_Display::unlock_display();
718
719 #else // SINGLE_THREAD
720
721
722
723 // Start tooltips
724         set_repeat(get_resources()->tooltip_delay);
725
726 // Start X server events
727         event_thread = new BC_WindowEvents(this);
728         event_thread->start();
729
730 // Release wait lock
731         window_running = 1;
732         init_lock->unlock();
733
734 // Handle common events
735         while( !done ) {
736                 dispatch_event();
737         }
738
739         unset_all_repeaters();
740         hide_tooltip();
741         delete event_thread;
742         event_thread = 0;
743         event_condition->reset();
744         common_events.remove_all_objects();
745         window_running = 0;
746         done = 0;
747
748 #endif // SINGLE_THREAD
749
750         return return_value;
751 }
752
753 int BC_WindowBase::get_key_masks(unsigned int key_state)
754 {
755 // printf("BC_WindowBase::get_key_masks %llx\n",
756 // event->xkey.state);
757         ctrl_mask = (key_state & ControlMask) ? 1 : 0;  // ctrl key down
758         shift_mask = (key_state & ShiftMask) ? 1 : 0;   // shift key down
759         alt_mask = (key_state & Mod1Mask) ? 1 : 0;      // alt key down
760         return 0;
761 }
762
763
764 void BC_WindowBase::add_keyboard_listener(int(BC_WindowBase::*handler)(BC_WindowBase *))
765 {
766         BC_KeyboardHandlerLock set;
767         BC_KeyboardHandler::listeners.append(new BC_KeyboardHandler(handler, this));
768 }
769
770 void BC_WindowBase::del_keyboard_listener(int(BC_WindowBase::*handler)(BC_WindowBase *))
771 {
772         BC_KeyboardHandlerLock set;
773         int i = BC_KeyboardHandler::listeners.size();
774         while( --i >= 0 && BC_KeyboardHandler::listeners[i]->handler!=handler );
775         if( i >= 0 ) BC_KeyboardHandler::listeners.remove_object_number(i);
776 }
777
778 int BC_KeyboardHandler::run_event(BC_WindowBase *wp)
779 {
780         int result = (win->*handler)(wp);
781         return result;
782 }
783
784 int BC_KeyboardHandler::run_listeners(BC_WindowBase *wp)
785 {
786         int result = 0;
787         BC_KeyboardHandlerLock set;
788         for( int i=0; !result && i<listeners.size(); ++i ) {
789                 BC_KeyboardHandler *listener = listeners[i];
790                 result = listener->run_event(wp);
791         }
792         return result;
793 }
794
795 void BC_KeyboardHandler::kill_grabs()
796 {
797         BC_KeyboardHandlerLock set;
798         for( int i=0; i<listeners.size(); ++i ) {
799                 BC_WindowBase *win = listeners[i]->win;
800                 if( win->get_window_type() != POPUP_WINDOW ) continue;
801                 ((BC_Popup *)win)->ungrab_keyboard();
802         }
803 }
804
805 void BC_ActiveBitmaps::reque(XEvent *event)
806 {
807         XShmCompletionEvent *shm_ev = (XShmCompletionEvent *)event;
808         ShmSeg shmseg = shm_ev->shmseg;
809         Drawable drawable = shm_ev->drawable;
810 //printf("BC_BitmapImage::reque %08lx\n",shmseg);
811         active_lock.lock("BC_BitmapImage::reque");
812         BC_BitmapImage *bfr = first;
813         while( bfr && bfr->get_shmseg() != shmseg ) bfr = bfr->next;
814         if( bfr && bfr->drawable == drawable )
815                 remove_pointer(bfr);
816         active_lock.unlock();
817         if( !bfr ) {
818 // sadly, X reports two drawable completions and creates false reporting, so no boobytrap
819 //              printf("BC_BitmapImage::reque missed shmseg %08x, drawable %08x\n",
820 //                       (int)shmseg, (int)drawable);
821                 return;
822         }
823         if( bfr->drawable != drawable ) return;
824         if( bfr->is_zombie() ) { --BC_Bitmap::zombies; delete bfr; return; }
825         bfr->bitmap->reque(bfr);
826 }
827
828 void BC_ActiveBitmaps::insert(BC_BitmapImage *bfr, Drawable pixmap)
829 {
830         active_lock.lock("BC_BitmapImage::insert");
831         bfr->drawable = pixmap;
832         append(bfr);
833         active_lock.unlock();
834 }
835
836 void BC_ActiveBitmaps::remove_buffers(BC_WindowBase *wdw)
837 {
838         active_lock.lock("BC_ActiveBitmaps::remove");
839         for( BC_BitmapImage *nxt=0, *bfr=first; bfr; bfr=nxt ) {
840                 nxt = bfr->next;
841                 if( bfr->is_zombie() ) { --BC_Bitmap::zombies; delete bfr; continue; }
842                 if( bfr->bitmap->parent_window == wdw ) remove_pointer(bfr);
843         }
844         active_lock.unlock();
845 }
846
847 BC_ActiveBitmaps::BC_ActiveBitmaps()
848 {
849 }
850
851 BC_ActiveBitmaps::~BC_ActiveBitmaps()
852 {
853 }
854
855
856
857 int BC_WindowBase::keysym_lookup(XEvent *event)
858 {
859         for( int i = 0; i < KEYPRESSLEN; ++i ) keys_return[i] = 0;
860         for( int i = 0; i < 4; ++i ) wkey_string[i] = 0;
861
862         if( event->xany.send_event && !event->xany.serial ) {
863                 keysym = (KeySym) event->xkey.keycode;
864                 keys_return[0] = keysym;
865                 return 0;
866         }
867         wkey_string_length = 0;
868
869         if( input_context ) {
870                 wkey_string_length = XwcLookupString(input_context,
871                         (XKeyEvent*)event, wkey_string, 4, &keysym, 0);
872 //printf("keysym_lookup 1 %d %d %lx %x %x %x %x\n", wkey_string_length, keysym,
873 //  wkey_string[0], wkey_string[1], wkey_string[2], wkey_string[3]);
874
875                 Status stat;
876                 int ret = Xutf8LookupString(input_context, (XKeyEvent*)event,
877                                 keys_return, KEYPRESSLEN, &keysym, &stat);
878 //printf("keysym_lookup 2 %d %d %lx %x %x\n", ret, stat, keysym, keys_return[0], keys_return[1]);
879                 if( stat == XLookupBoth ) return ret;
880                 if( stat == XLookupKeySym ) return 0;
881         }
882         int ret = XLookupString((XKeyEvent*)event, keys_return, KEYPRESSLEN, &keysym, 0);
883         wkey_string_length = ret;
884         for( int i=0; i<ret; ++i ) wkey_string[i] = keys_return[i];
885         return ret;
886 }
887
888 pthread_t locking_task = (pthread_t)-1L;
889 int locking_event = -1;
890 int locking_message = -1;
891
892 int BC_WindowBase::dispatch_event()
893 {
894         Window tempwin;
895         int result;
896         XClientMessageEvent *ptr;
897         int cancel_resize, cancel_translation;
898         volatile static int debug = 0;
899         XEvent *event;
900
901         key_pressed = 0;
902
903 #ifndef SINGLE_THREAD
904 // If an event is waiting get it, otherwise
905 // wait for next event only if there are no compressed events.
906         if(get_event_count() ||
907                 (!motion_events && !resize_events && !translation_events))
908         {
909                 event = get_event();
910 // Lock out window deletions
911                 lock_window("BC_WindowBase::dispatch_event 1");
912 locking_event = event->type;
913 locking_task = pthread_self();
914 locking_message = event->xclient.message_type;
915         }
916         else
917 // Handle compressed events
918         {
919                 lock_window("BC_WindowBase::dispatch_event 2");
920                 if(resize_events)
921                         dispatch_resize_event(last_resize_w, last_resize_h);
922                 if(motion_events)
923                         dispatch_motion_event();
924                 if(translation_events)
925                         dispatch_translation_event();
926
927                 unlock_window();
928                 return 0;
929         }
930
931 #endif
932
933
934
935
936 if( debug && event->type != ClientMessage ) {
937  static const char *event_names[] = {
938   "Reply", "Error", "KeyPress", "KeyRelease", "ButtonPress", "ButtonRelease", "MotionNotify",
939   "EnterNotify", "LeaveNotify", "FocusIn", "FocusOut", "KeymapNotify", "Expose", "GraphicsExpose",
940   "NoExpose", "VisibilityNotify", "CreateNotify", "DestroyNotify", "UnmapNotify", "MapNotify",
941   "MapRequest", "ReparentNotify", "ConfigureNotify", "ConfigureRequest", "GravityNotify",
942   "ResizeRequest", "CirculateNotify", "CirculateRequest", "PropertyNotify", "SelectionClear",
943   "SelectionRequest", "SelectionNotify", "ColormapNotify", "ClientMessage", "MappingNotify",
944   "GenericEvent", "LASTEvent",
945  };
946  const int nevents = sizeof(event_names)/sizeof(event_names[0]);
947
948  printf("BC_WindowBase::dispatch_event %d %s %p %d (%s)\n", __LINE__,
949   title, event, event->type, event->type>=0 && event->type<nevents ?
950    event_names[event->type] : "Unknown");
951 }
952
953         if( active_grab ) {
954                 unlock_window();
955                 active_grab->lock_window("BC_WindowBase::dispatch_event 3");
956                 result = active_grab->grab_event(event);
957                 active_grab->unlock_window();
958                 if( result ) return result;
959                 lock_window("BC_WindowBase::dispatch_event 4");
960         }
961
962         switch(event->type) {
963         case ClientMessage:
964 // Clear the resize buffer
965                 if( resize_events )
966                         dispatch_resize_event(last_resize_w, last_resize_h);
967 // Clear the motion buffer since this can clear the window
968                 if( motion_events )
969                         dispatch_motion_event();
970
971                 ptr = (XClientMessageEvent*)event;
972                 if( ptr->message_type == ProtoXAtom &&
973                     (Atom)ptr->data.l[0] == DelWinXAtom ) {
974                         close_event();
975                 }
976                 else if( ptr->message_type == RepeaterXAtom ) {
977                         dispatch_repeat_event(ptr->data.l[0]);
978                 }
979                 else if( ptr->message_type == SetDoneXAtom ) {
980                         done = 1;
981                 }
982                 else {
983                         receive_custom_xatoms((xatom_event *)ptr);
984                 }
985                 break;
986
987         case FocusIn:
988                 has_focus = 1;
989                 dispatch_focus_in();
990                 break;
991
992         case FocusOut:
993                 has_focus = 0;
994                 dispatch_focus_out();
995                 break;
996
997 // Maximized
998         case MapNotify:
999                 break;
1000
1001 // Minimized
1002         case UnmapNotify:
1003                 break;
1004
1005         case ButtonPress:
1006                 if(motion_events)
1007                 {
1008                         dispatch_motion_event();
1009                 }
1010                 get_key_masks(event->xbutton.state);
1011                 cursor_x = event->xbutton.x;
1012                 cursor_y = event->xbutton.y;
1013                 button_number = event->xbutton.button;
1014
1015 //printf("BC_WindowBase::dispatch_event %d %d\n", __LINE__, button_number);
1016                 event_win = event->xany.window;
1017                 if (button_number < 6) {
1018                         if(button_number < 4)
1019                                 button_down = 1;
1020                         button_pressed = event->xbutton.button;
1021                         button_time1 = button_time2;
1022                         button_time2 = button_time3;
1023                         button_time3 = event->xbutton.time;
1024                         drag_x = cursor_x;
1025                         drag_y = cursor_y;
1026                         drag_win = event_win;
1027                         drag_x1 = cursor_x - get_resources()->drag_radius;
1028                         drag_x2 = cursor_x + get_resources()->drag_radius;
1029                         drag_y1 = cursor_y - get_resources()->drag_radius;
1030                         drag_y2 = cursor_y + get_resources()->drag_radius;
1031
1032                         if((long)(button_time3 - button_time1) < resources.double_click * 2)
1033                         {
1034                                 triple_click = 1;
1035                                 button_time3 = button_time2 = button_time1 = 0;
1036                         }
1037                         if((long)(button_time3 - button_time2) < resources.double_click)
1038                         {
1039                                 double_click = 1;
1040 //                              button_time3 = button_time2 = button_time1 = 0;
1041                         }
1042                         else
1043                         {
1044                                 triple_click = 0;
1045                                 double_click = 0;
1046                         }
1047
1048                         dispatch_button_press();
1049                 }
1050                 break;
1051
1052         case ButtonRelease:
1053                 if(motion_events)
1054                 {
1055                         dispatch_motion_event();
1056                 }
1057                 get_key_masks(event->xbutton.state);
1058                 button_number = event->xbutton.button;
1059                 event_win = event->xany.window;
1060                 if (button_number < 6)
1061                 {
1062                         if(button_number < 4)
1063                                 button_down = 0;
1064 //printf("BC_WindowBase::dispatch_event %d %d\n", __LINE__, button_number);
1065
1066                         dispatch_button_release();
1067                 }
1068                 break;
1069
1070         case Expose:
1071                 event_win = event->xany.window;
1072                 result = 0;
1073                 for( int i=0; !result && i<popups.size(); ++i ) {  // popups take focus
1074                         if( popups[i]->win == event_win )
1075                                 result = popups[i]->dispatch_expose_event();
1076                 }
1077                 if( !result )
1078                         result = dispatch_expose_event();
1079                 break;
1080
1081         case MotionNotify:
1082                 get_key_masks(event->xmotion.state);
1083 // Dispatch previous motion event if this is a subsequent motion from a different window
1084                 if(motion_events && last_motion_win != event->xany.window)
1085                 {
1086                         dispatch_motion_event();
1087                 }
1088
1089 // Buffer the current motion
1090                 motion_events = 1;
1091                 last_motion_state = event->xmotion.state;
1092                 last_motion_x = event->xmotion.x;
1093                 last_motion_y = event->xmotion.y;
1094                 last_motion_win = event->xany.window;
1095                 break;
1096
1097         case ConfigureNotify:
1098 // printf("BC_WindowBase::dispatch_event %d win=%p this->win=%p\n",
1099 // __LINE__,
1100 // event->xany.window,
1101 // win);
1102 // dump_windows();
1103                 XTranslateCoordinates(top_level->display,
1104                         top_level->win,
1105                         top_level->rootwin,
1106                         0,
1107                         0,
1108                         &last_translate_x,
1109                         &last_translate_y,
1110                         &tempwin);
1111                 last_resize_w = event->xconfigure.width;
1112                 last_resize_h = event->xconfigure.height;
1113
1114                 cancel_resize = 0;
1115                 cancel_translation = 0;
1116
1117 // Resize history prevents responses to recursive resize requests
1118                 for(int i = 0; i < resize_history.total && !cancel_resize; i++)
1119                 {
1120                         if(resize_history.values[i]->w == last_resize_w &&
1121                                 resize_history.values[i]->h == last_resize_h)
1122                         {
1123                                 delete resize_history.values[i];
1124                                 resize_history.remove_number(i);
1125                                 cancel_resize = 1;
1126                         }
1127                 }
1128
1129                 if(last_resize_w == w && last_resize_h == h)
1130                         cancel_resize = 1;
1131
1132                 if(!cancel_resize)
1133                 {
1134                         resize_events = 1;
1135                 }
1136
1137                 if((last_translate_x == x && last_translate_y == y))
1138                         cancel_translation = 1;
1139
1140                 if(!cancel_translation)
1141                 {
1142                         translation_events = 1;
1143                 }
1144
1145                 translation_count++;
1146                 break;
1147
1148         case KeyPress:
1149                 get_key_masks(event->xkey.state);
1150                 keys_return[0] = 0;  keysym = -1;
1151                 if(XFilterEvent(event, win)) {
1152                         break;
1153                 }
1154                 if( keysym_lookup(event) < 0 ) {
1155                         printf("keysym %x\n", (uint32_t)keysym);
1156                         break;
1157                 }
1158
1159 //printf("BC_WindowBase::dispatch_event %d keysym=0x%x\n",
1160 //__LINE__,
1161 //keysym);
1162
1163 // block out control keys
1164                 if(keysym > 0xffe0 && keysym < 0xffff) break;
1165 // block out Alt_GR key
1166                 if(keysym == 0xfe03) break;
1167
1168                 if(test_keypress)
1169                          printf("BC_WindowBase::dispatch_event %x\n", (uint32_t)keysym);
1170
1171 #ifdef X_HAVE_UTF8_STRING
1172 //It's Ascii or UTF8?
1173 //              if (keysym != 0xffff && (keys_return[0] & 0xff) >= 0x7f )
1174 //printf("BC_WindowBase::dispatch_event %d %02x%02x\n", __LINE__, keys_return[0], keys_return[1]);
1175
1176                 if( ((keys_return[1] & 0xff) > 0x80) &&
1177                     ((keys_return[0] & 0xff) > 0xC0) ) {
1178 //printf("BC_WindowBase::dispatch_event %d\n", __LINE__);
1179                         key_pressed = keysym & 0xff;
1180                 }
1181                 else {
1182 #endif
1183 // shuttle speed codes
1184                 if( keysym >= SKEY_MIN && keysym <= SKEY_MAX ) {
1185                         key_pressed = keysym;
1186                 }
1187                 else switch( keysym ) {
1188 // block out extra keys
1189                 case XK_Alt_L:
1190                 case XK_Alt_R:
1191                 case XK_Shift_L:
1192                 case XK_Shift_R:
1193                 case XK_Control_L:
1194                 case XK_Control_R:
1195                         key_pressed = 0;
1196                         break;
1197
1198 // Translate key codes
1199                 case XK_Return:         key_pressed = RETURN;   break;
1200                 case XK_Up:             key_pressed = UP;       break;
1201                 case XK_Down:           key_pressed = DOWN;     break;
1202                 case XK_Left:           key_pressed = LEFT;     break;
1203                 case XK_Right:          key_pressed = RIGHT;    break;
1204                 case XK_Next:           key_pressed = PGDN;     break;
1205                 case XK_Prior:          key_pressed = PGUP;     break;
1206                 case XK_BackSpace:      key_pressed = BACKSPACE; break;
1207                 case XK_Escape:         key_pressed = ESC;      break;
1208                 case XK_Tab:
1209                         if(shift_down())
1210                                 key_pressed = LEFTTAB;
1211                         else
1212                                 key_pressed = TAB;
1213                         break;
1214                 case XK_ISO_Left_Tab:   key_pressed = LEFTTAB;  break;
1215                 case XK_underscore:     key_pressed = '_';      break;
1216                 case XK_asciitilde:     key_pressed = '~';      break;
1217                 case XK_Delete:         key_pressed = DELETE;   break;
1218                 case XK_Home:           key_pressed = HOME;     break;
1219                 case XK_End:            key_pressed = END;      break;
1220
1221 // number pad
1222                 case XK_KP_Enter:       key_pressed = KPENTER;  break;
1223                 case XK_KP_Add:         key_pressed = KPPLUS;   break;
1224                 case XK_KP_Subtract:    key_pressed = KPMINUS;  break;
1225                 case XK_KP_Multiply:    key_pressed = KPSTAR;   break;
1226                 case XK_KP_Divide:      key_pressed = KPSLASH;  break;
1227                 case XK_KP_1:
1228                 case XK_KP_End:         key_pressed = KP1;      break;
1229                 case XK_KP_2:
1230                 case XK_KP_Down:        key_pressed = KP2;      break;
1231                 case XK_KP_3:
1232                 case XK_KP_Page_Down:   key_pressed = KP3;      break;
1233                 case XK_KP_4:
1234                 case XK_KP_Left:        key_pressed = KP4;      break;
1235                 case XK_KP_5:
1236                 case XK_KP_Begin:       key_pressed = KP5;      break;
1237                 case XK_KP_6:
1238                 case XK_KP_Right:       key_pressed = KP6;      break;
1239                 case XK_KP_7:
1240                 case XK_KP_Home:        key_pressed = KP7;      break;
1241                 case XK_KP_8:
1242                 case XK_KP_Up:          key_pressed = KP8;      break;
1243                 case XK_KP_9:
1244                 case XK_KP_Page_Up:     key_pressed = KP9;      break;
1245                 case XK_KP_0:
1246                 case XK_KP_Insert:      key_pressed = KPINS;    break;
1247                 case XK_KP_Decimal:
1248                 case XK_KP_Delete:      key_pressed = KPDEL;    break;
1249
1250                 case XK_F1:             key_pressed = KEY_F1;   break;
1251                 case XK_F2:             key_pressed = KEY_F2;   break;
1252                 case XK_F3:             key_pressed = KEY_F3;   break;
1253                 case XK_F4:             key_pressed = KEY_F4;   break;
1254                 case XK_F5:             key_pressed = KEY_F5;   break;
1255                 case XK_F6:             key_pressed = KEY_F6;   break;
1256                 case XK_F7:             key_pressed = KEY_F7;   break;
1257                 case XK_F8:             key_pressed = KEY_F8;   break;
1258                 case XK_F9:             key_pressed = KEY_F9;   break;
1259                 case XK_F10:            key_pressed = KEY_F10;  break;
1260                 case XK_F11:            key_pressed = KEY_F11;  break;
1261                 case XK_F12:            key_pressed = KEY_F12;  break;
1262
1263                 case XK_Menu:           key_pressed = KPMENU;   break;  /* menu */
1264 // remote control
1265 // above        case XK_KP_Enter:       key_pressed = KPENTER;  break;  /* check */
1266                 case XF86XK_MenuKB:     key_pressed = KPMENU;   break;  /* menu */
1267 // intercepted  case XF86XK_PowerDown: key_pressed = KPPOWER;   break;  /* Power */
1268                 case XF86XK_Launch1:    key_pressed = KPTV;     break;  /* TV */
1269                 case XF86XK_Launch2:    key_pressed = KPDVD;    break;  /* DVD */
1270 // intercepted  case XF86XK_WWW:        key_pressed = KPWWEB;   break;  /* WEB */
1271                 case XF86XK_Launch3:    key_pressed = KPBOOK;   break;  /* book */
1272                 case XF86XK_Launch4:    key_pressed = KPHAND;   break;  /* hand */
1273                 case XF86XK_Reply:      key_pressed = KPTMR;    break;  /* timer */
1274                 case SunXK_Front:       key_pressed = KPMAXW;   break;  /* max */
1275 // above        case XK_Left:           key_pressed = LEFT;     break;  /* left */
1276 // above        case XK_Right:          key_pressed = RIGHT;    break;  /* right */
1277 // above        case XK_Down:           key_pressed = DOWN;     break;  /* down */
1278 // above        case XK_Up:             key_pressed = UP;       break;  /* up */
1279 // above        case XK_SPACE:          key_pressed = KPSPACE;  break;  /* ok */
1280 // intercepted  case XF86XK_AudioRaiseVolume: key_pressed = KPVOLU;     break;  /* VOL + */
1281 // intercepted  case XF86XK_AudioMute: key_pressed = KPMUTE;    break;  /* MUTE */
1282 // intercepted  case XF86XK_AudioLowerVolume: key_pressed = KPVOLD;     break;  /* VOL - */
1283                 case XF86XK_ScrollUp:   key_pressed = KPCHUP;   break;  /* CH + */
1284                 case XF86XK_ScrollDown: key_pressed = KPCHDN;   break;  /* CH - */
1285                 case XF86XK_AudioRecord: key_pressed = KPRECD;  break;  /* ( o) red */
1286                 case XF86XK_Forward:    key_pressed = KPPLAY;   break;  /* ( >) */
1287                 case XK_Redo:           key_pressed = KPFWRD;   break;  /* (>>) */
1288                 case XF86XK_Back:       key_pressed = KPBACK;   break;  /* (<<) */
1289                 case XK_Cancel:         key_pressed = KPSTOP;   break;  /* ([]) */
1290                 case XK_Pause:          key_pressed = KPAUSE;   break;  /* ('') */
1291
1292                 default:
1293                         key_pressed = keysym & 0xff;
1294 #ifdef X_HAVE_UTF8_STRING
1295 //printf("BC_WindowBase::dispatch_event %d\n", __LINE__);
1296                         keys_return[1] = 0;
1297 #endif
1298                         break;
1299                 }
1300 #ifdef X_HAVE_UTF8_STRING
1301                 }
1302                 key_pressed_utf8 = keys_return;
1303 #endif
1304
1305
1306                 result = 0;
1307                 if( top_level == this )
1308                         result = BC_KeyboardHandler::run_listeners(this);
1309
1310 //printf("BC_WindowBase::dispatch_event %d %d %x\n", shift_down(), alt_down(), key_pressed);
1311                 if( !result )
1312                         result = dispatch_keypress_event();
1313 // Handle some default keypresses
1314                 if(!result)
1315                 {
1316                         if(key_pressed == 'w' ||
1317                                 key_pressed == 'W')
1318                         {
1319                                 close_event();
1320                         }
1321                 }
1322                 break;
1323
1324         case KeyRelease:
1325                 XLookupString((XKeyEvent*)event, keys_return, 1, &keysym, 0);
1326                 dispatch_keyrelease_event();
1327 // printf("BC_WindowBase::dispatch_event KeyRelease keysym=0x%x keystate=0x%lld\n",
1328 // keysym, event->xkey.state);
1329                 break;
1330
1331         case LeaveNotify:
1332                 if( event->xcrossing.mode != NotifyNormal ) break;
1333                 cursor_entered = 0;
1334                 event_win = event->xany.window;
1335                 dispatch_cursor_leave();
1336                 break;
1337
1338         case EnterNotify:
1339                 if( event->xcrossing.mode != NotifyNormal ) break;
1340
1341                 if( !cursor_entered ) {
1342                         for( int i=0; i<popups.size(); ++i ) {  // popups always take focus
1343                                 if( popups[i]->win == event->xcrossing.window )
1344                                 cursor_entered = 1;
1345                         }
1346                         if( !cursor_entered && get_resources()->grab_input_focus &&
1347                             !event->xcrossing.focus && event->xcrossing.window == win ) {
1348                                 cursor_entered = 1;
1349                         }
1350                         if( cursor_entered )
1351                                 focus();
1352                 }
1353                 event_win = event->xany.window;
1354                 cursor_x = event->xcrossing.x;
1355                 cursor_y = event->xcrossing.y;
1356                 dispatch_cursor_enter();
1357                 break;
1358
1359         default:
1360                 break;
1361         }
1362 //printf("100 %s %p %d\n", title, event, event->type);
1363 //if(event->type != ClientMessage) dump();
1364
1365 #ifndef SINGLE_THREAD
1366         unlock_window();
1367         if(event) {
1368                 if( resend_event_window ) {
1369                         resend_event_window->put_event(event);
1370                         resend_event_window = 0;
1371                 }
1372                 else
1373                         delete event;
1374         }
1375 #else
1376 //      if(done) completion_lock->unlock();
1377 #endif
1378
1379 if(debug) printf("BC_WindowBase::dispatch_event this=%p %d\n", this, __LINE__);
1380         return 0;
1381 }
1382
1383 int BC_WindowBase::dispatch_expose_event()
1384 {
1385         int result = 0;
1386         for(int i = 0; i < subwindows->total && !result; i++)
1387         {
1388                 result = subwindows->values[i]->dispatch_expose_event();
1389         }
1390
1391 // Propagate to user
1392         if(!result) expose_event();
1393         return result;
1394 }
1395
1396 int BC_WindowBase::dispatch_resize_event(int w, int h)
1397 {
1398 // Can't store new w and h until the event is handles
1399 // because bcfilebox depends on the old w and h to
1400 // reposition widgets.
1401         if( window_type == MAIN_WINDOW ) {
1402                 flash_enabled = 0;
1403                 resize_events = 0;
1404
1405                 delete pixmap;
1406                 pixmap = new BC_Pixmap(this, w, h);
1407                 clear_box(0, 0, w, h);
1408         }
1409
1410 // Propagate to subwindows
1411         for(int i = 0; i < subwindows->total; i++) {
1412                 subwindows->values[i]->dispatch_resize_event(w, h);
1413         }
1414
1415 // Propagate to user
1416         resize_event(w, h);
1417
1418         if( window_type == MAIN_WINDOW ) {
1419                 this->w = w;
1420                 this->h = h;
1421                 dispatch_flash();
1422                 flush();
1423         }
1424         return 0;
1425 }
1426
1427 int BC_WindowBase::dispatch_flash()
1428 {
1429         flash_enabled = 1;
1430         for(int i = 0; i < subwindows->total; i++)
1431                 subwindows->values[i]->dispatch_flash();
1432         return flash(0);
1433 }
1434
1435 int BC_WindowBase::dispatch_translation_event()
1436 {
1437         translation_events = 0;
1438         if(window_type == MAIN_WINDOW)
1439         {
1440                 prev_x = x;
1441                 prev_y = y;
1442                 x = last_translate_x;
1443                 y = last_translate_y;
1444 // Correct for window manager offsets
1445                 x -= x_correction;
1446                 y -= y_correction;
1447         }
1448
1449         for(int i = 0; i < subwindows->total; i++)
1450         {
1451                 subwindows->values[i]->dispatch_translation_event();
1452         }
1453
1454         translation_event();
1455         return 0;
1456 }
1457
1458 int BC_WindowBase::dispatch_motion_event()
1459 {
1460         int result = 0;
1461         unhide_cursor();
1462
1463         if(top_level == this)
1464         {
1465                 motion_events = 0;
1466                 event_win = last_motion_win;
1467                 get_key_masks(last_motion_state);
1468
1469 // Test for grab
1470                 if(get_button_down() && !active_menubar && !active_popup_menu)
1471                 {
1472                         if(!result)
1473                         {
1474                                 cursor_x = last_motion_x;
1475                                 cursor_y = last_motion_y;
1476                                 result = dispatch_drag_motion();
1477                         }
1478
1479                         if(!result &&
1480                                 (last_motion_x < drag_x1 || last_motion_x >= drag_x2 ||
1481                                 last_motion_y < drag_y1 || last_motion_y >= drag_y2))
1482                         {
1483                                 cursor_x = drag_x;
1484                                 cursor_y = drag_y;
1485
1486                                 result = dispatch_drag_start();
1487                         }
1488                 }
1489
1490                 cursor_x = last_motion_x;
1491                 cursor_y = last_motion_y;
1492
1493 // printf("BC_WindowBase::dispatch_motion_event %d %p %p %p\n",
1494 // __LINE__,
1495 // active_menubar,
1496 // active_popup_menu,
1497 // active_subwindow);
1498
1499                 if(active_menubar && !result) result = active_menubar->dispatch_motion_event();
1500                 if(active_popup_menu && !result) result = active_popup_menu->dispatch_motion_event();
1501                 if(active_subwindow && !result) result = active_subwindow->dispatch_motion_event();
1502         }
1503
1504 // Dispatch in stacking order
1505         for(int i = subwindows->size() - 1; i >= 0 && !result; i--)
1506         {
1507                 result = subwindows->values[i]->dispatch_motion_event();
1508         }
1509
1510         if(!result) result = cursor_motion_event();    // give to user
1511         return result;
1512 }
1513
1514 int BC_WindowBase::dispatch_keypress_event()
1515 {
1516         int result = 0;
1517         if(top_level == this)
1518         {
1519                 if(active_subwindow) result = active_subwindow->dispatch_keypress_event();
1520         }
1521
1522         for(int i = 0; i < subwindows->total && !result; i++)
1523         {
1524                 result = subwindows->values[i]->dispatch_keypress_event();
1525         }
1526
1527         if(!result) result = keypress_event();
1528
1529         return result;
1530 }
1531
1532 int BC_WindowBase::dispatch_keyrelease_event()
1533 {
1534         int result = 0;
1535         if(top_level == this)
1536         {
1537                 if(active_subwindow) result = active_subwindow->dispatch_keyrelease_event();
1538         }
1539
1540         for(int i = 0; i < subwindows->total && !result; i++)
1541         {
1542                 result = subwindows->values[i]->dispatch_keyrelease_event();
1543         }
1544
1545         if(!result) result = keyrelease_event();
1546
1547         return result;
1548 }
1549
1550 int BC_WindowBase::dispatch_focus_in()
1551 {
1552         for(int i = 0; i < subwindows->total; i++)
1553         {
1554                 subwindows->values[i]->dispatch_focus_in();
1555         }
1556
1557         focus_in_event();
1558
1559         return 0;
1560 }
1561
1562 int BC_WindowBase::dispatch_focus_out()
1563 {
1564         for(int i = 0; i < subwindows->total; i++)
1565         {
1566                 subwindows->values[i]->dispatch_focus_out();
1567         }
1568
1569         focus_out_event();
1570
1571         return 0;
1572 }
1573
1574 int BC_WindowBase::get_has_focus()
1575 {
1576         return top_level->has_focus;
1577 }
1578
1579 int BC_WindowBase::get_deleting()
1580 {
1581         if(is_deleting) return 1;
1582         if(parent_window && parent_window->get_deleting()) return 1;
1583         return 0;
1584 }
1585
1586 int BC_WindowBase::dispatch_button_press()
1587 {
1588         int result = 0;
1589
1590
1591         if(top_level == this)
1592         {
1593                 if(active_menubar) result = active_menubar->dispatch_button_press();
1594                 if(active_popup_menu && !result) result = active_popup_menu->dispatch_button_press();
1595                 if(active_subwindow && !result) result = active_subwindow->dispatch_button_press();
1596         }
1597
1598         for(int i = 0; i < subwindows->total && !result; i++)
1599         {
1600                 result = subwindows->values[i]->dispatch_button_press();
1601         }
1602
1603         if(!result) result = button_press_event();
1604
1605
1606         return result;
1607 }
1608
1609 int BC_WindowBase::dispatch_button_release()
1610 {
1611         int result = 0;
1612         if(top_level == this)
1613         {
1614                 if(active_menubar) result = active_menubar->dispatch_button_release();
1615                 if(active_popup_menu && !result) result = active_popup_menu->dispatch_button_release();
1616                 if(active_subwindow && !result) result = active_subwindow->dispatch_button_release();
1617                 if(!result && button_number != 4 && button_number != 5)
1618                         result = dispatch_drag_stop();
1619         }
1620
1621         for(int i = 0; i < subwindows->total && !result; i++)
1622         {
1623                 result = subwindows->values[i]->dispatch_button_release();
1624         }
1625
1626         if(!result)
1627         {
1628                 result = button_release_event();
1629         }
1630
1631         return result;
1632 }
1633
1634
1635 int BC_WindowBase::dispatch_repeat_event(int64_t duration)
1636 {
1637
1638 // all repeat event handlers get called and decide based on activity and duration
1639 // whether to respond
1640         for(int i = 0; i < subwindows->total; i++)
1641         {
1642                 subwindows->values[i]->dispatch_repeat_event(duration);
1643         }
1644
1645
1646         repeat_event(duration);
1647
1648
1649
1650 // Unlock next repeat signal
1651         if(window_type == MAIN_WINDOW)
1652         {
1653 #ifdef SINGLE_THREAD
1654                 BC_Display::display_global->unlock_repeaters(duration);
1655 #else
1656                 for(int i = 0; i < repeaters.total; i++)
1657                 {
1658                         if(repeaters.values[i]->delay == duration)
1659                         {
1660                                 repeaters.values[i]->repeat_lock->unlock();
1661                         }
1662                 }
1663 #endif
1664         }
1665         return 0;
1666 }
1667
1668 void BC_WindowBase::unhide_cursor()
1669 {
1670         if(is_transparent)
1671         {
1672                 is_transparent = 0;
1673                 if(top_level->is_hourglass)
1674                         set_cursor(HOURGLASS_CURSOR, 1, 0);
1675                 else
1676                         set_cursor(current_cursor, 1, 0);
1677         }
1678         cursor_timer->update();
1679 }
1680
1681
1682 void BC_WindowBase::update_video_cursor()
1683 {
1684         if(video_on && !is_transparent)
1685         {
1686                 if(cursor_timer->get_difference() > VIDEO_CURSOR_TIMEOUT && !is_transparent)
1687                 {
1688                         is_transparent = 1;
1689                         set_cursor(TRANSPARENT_CURSOR, 1, 1);
1690                         cursor_timer->update();
1691                 }
1692         }
1693         else
1694         {
1695                 cursor_timer->update();
1696         }
1697 }
1698
1699
1700 int BC_WindowBase::dispatch_cursor_leave()
1701 {
1702         unhide_cursor();
1703
1704         for(int i = 0; i < subwindows->total; i++)
1705         {
1706                 subwindows->values[i]->dispatch_cursor_leave();
1707         }
1708
1709         cursor_leave_event();
1710         return 0;
1711 }
1712
1713 int BC_WindowBase::dispatch_cursor_enter()
1714 {
1715         int result = 0;
1716
1717         unhide_cursor();
1718
1719         if(active_menubar) result = active_menubar->dispatch_cursor_enter();
1720         if(!result && active_popup_menu) result = active_popup_menu->dispatch_cursor_enter();
1721         if(!result && active_subwindow) result = active_subwindow->dispatch_cursor_enter();
1722
1723         for(int i = 0; !result && i < subwindows->total; i++)
1724         {
1725                 result = subwindows->values[i]->dispatch_cursor_enter();
1726         }
1727
1728         if(!result) result = cursor_enter_event();
1729         return result;
1730 }
1731
1732 int BC_WindowBase::cursor_enter_event()
1733 {
1734         return 0;
1735 }
1736
1737 int BC_WindowBase::cursor_leave_event()
1738 {
1739         return 0;
1740 }
1741
1742 int BC_WindowBase::close_event()
1743 {
1744         set_done(1);
1745         return 1;
1746 }
1747
1748 int BC_WindowBase::dispatch_drag_start()
1749 {
1750         int result = 0;
1751         if(active_menubar) result = active_menubar->dispatch_drag_start();
1752         if(!result && active_popup_menu) result = active_popup_menu->dispatch_drag_start();
1753         if(!result && active_subwindow) result = active_subwindow->dispatch_drag_start();
1754
1755         for(int i = 0; i < subwindows->total && !result; i++)
1756         {
1757                 result = subwindows->values[i]->dispatch_drag_start();
1758         }
1759
1760         if(!result) result = is_dragging = drag_start_event();
1761         return result;
1762 }
1763
1764 int BC_WindowBase::dispatch_drag_stop()
1765 {
1766         int result = 0;
1767
1768         for(int i = 0; i < subwindows->total && !result; i++)
1769         {
1770                 result = subwindows->values[i]->dispatch_drag_stop();
1771         }
1772
1773         if(is_dragging && !result)
1774         {
1775                 drag_stop_event();
1776                 is_dragging = 0;
1777                 result = 1;
1778         }
1779
1780         return result;
1781 }
1782
1783 int BC_WindowBase::dispatch_drag_motion()
1784 {
1785         int result = 0;
1786         for(int i = 0; i < subwindows->total && !result; i++)
1787         {
1788                 result = subwindows->values[i]->dispatch_drag_motion();
1789         }
1790
1791         if(is_dragging && !result)
1792         {
1793                 drag_motion_event();
1794                 result = 1;
1795         }
1796
1797         return result;
1798 }
1799
1800
1801 int BC_WindowBase::show_tooltip(const char *text, int x, int y, int w, int h)
1802 {
1803 // default text
1804         int forced = !text ? force_tooltip : 1;
1805         if( !text ) text = tooltip_text;
1806         if( !text || (!forced && !get_resources()->tooltips_enabled) ) {
1807                 top_level->hide_tooltip();
1808                 return 1;
1809         }
1810 // default w,h
1811         if(w < 0) w = get_text_width(MEDIUMFONT, text)  + TOOLTIP_MARGIN * 2;
1812         if(h < 0) h = get_text_height(MEDIUMFONT, text) + TOOLTIP_MARGIN * 2;
1813 // default x,y (win relative)
1814         if( x < 0 ) x = get_w();
1815         if( y < 0 ) y = get_h();
1816         int wx, wy;
1817         get_root_coordinates(x, y, &wx, &wy);
1818 // keep the tip inside the window/display
1819         int x0 = top_level->get_x(), x1 = x0 + top_level->get_w();
1820         int x2 = top_level->get_screen_x(0, -1) + top_level->get_screen_w(0, -1);
1821         if( x1 > x2 ) x1 = x2;
1822         if( wx < x0 ) wx = x0;
1823         if( wx >= (x1-=w) ) wx = x1;
1824         int y0 = top_level->get_y(), y1 = y0 + top_level->get_h();
1825         int y2 = top_level->get_root_h(0);
1826         if( y1 > y2 ) y1 = y2;
1827         if( wy < y0 ) wy = y0;
1828         if( wy >= (y1-=h) ) wy = y1;
1829 // avoid tip under cursor (flickers)
1830         int abs_x, abs_y;
1831         get_abs_cursor(abs_x,abs_y, 0);
1832         if( wx < abs_x && abs_x < wx+w && wy < abs_y && abs_y < wy+h ) {
1833                 if( wx-abs_x < wy-abs_y )
1834                         wx = abs_x+1;
1835                 else
1836                         wy = abs_y+1;
1837         }
1838         if( !tooltip_on ) {
1839                 tooltip_on = 1;
1840                 tooltip_popup = new BC_Popup(top_level, wx, wy, w, h,
1841                                 get_resources()->tooltip_bg_color);
1842         }
1843         else
1844                 tooltip_popup->reposition_window(wx, wy, w, h);
1845
1846         draw_tooltip(text);
1847         tooltip_popup->flash();
1848         tooltip_popup->flush();
1849         return 0;
1850 }
1851
1852 int BC_WindowBase::hide_tooltip()
1853 {
1854         if(subwindows)
1855                 for(int i = 0; i < subwindows->total; i++)
1856                 {
1857                         subwindows->values[i]->hide_tooltip();
1858                 }
1859
1860         if(tooltip_on)
1861         {
1862                 tooltip_on = 0;
1863                 delete tooltip_popup;
1864                 tooltip_popup = 0;
1865         }
1866         return 0;
1867 }
1868
1869 const char *BC_WindowBase::get_tooltip()
1870 {
1871         return tooltip_text;
1872 }
1873
1874 int BC_WindowBase::set_tooltip(const char *text)
1875 {
1876         tooltip_text = text;
1877
1878 // Update existing tooltip if it is visible
1879         if(tooltip_on)
1880         {
1881                 draw_tooltip();
1882                 tooltip_popup->flash();
1883         }
1884         return 0;
1885 }
1886 // signal the event handler to repeat
1887 int BC_WindowBase::set_repeat(int64_t duration)
1888 {
1889         if(duration <= 0)
1890         {
1891                 printf("BC_WindowBase::set_repeat duration=%jd\n", duration);
1892                 return 0;
1893         }
1894         if(window_type != MAIN_WINDOW) return top_level->set_repeat(duration);
1895
1896 #ifdef SINGLE_THREAD
1897         BC_Display::display_global->set_repeat(this, duration);
1898 #else
1899 // test repeater database for duplicates
1900         for(int i = 0; i < repeaters.total; i++)
1901         {
1902 // Already exists
1903                 if(repeaters.values[i]->delay == duration)
1904                 {
1905                         repeaters.values[i]->start_repeating(this);
1906                         return 0;
1907                 }
1908         }
1909
1910         BC_Repeater *repeater = new BC_Repeater(this, duration);
1911         repeater->initialize();
1912         repeaters.append(repeater);
1913         repeater->start_repeating();
1914 #endif
1915         return 0;
1916 }
1917
1918 int BC_WindowBase::unset_repeat(int64_t duration)
1919 {
1920         if(window_type != MAIN_WINDOW) return top_level->unset_repeat(duration);
1921
1922 #ifdef SINGLE_THREAD
1923         BC_Display::display_global->unset_repeat(this, duration);
1924 #else
1925         for(int i = 0; i < repeaters.total; i++)
1926         {
1927                 if(repeaters.values[i]->delay == duration)
1928                 {
1929                         repeaters.values[i]->stop_repeating();
1930                 }
1931         }
1932 #endif
1933         return 0;
1934 }
1935
1936
1937 int BC_WindowBase::unset_all_repeaters()
1938 {
1939 #ifdef SINGLE_THREAD
1940         BC_Display::display_global->unset_all_repeaters(this);
1941 #else
1942         for(int i = 0; i < repeaters.total; i++)
1943         {
1944                 repeaters.values[i]->stop_repeating();
1945         }
1946         repeaters.remove_all_objects();
1947 #endif
1948         return 0;
1949 }
1950
1951 // long BC_WindowBase::get_repeat_id()
1952 // {
1953 //      return top_level->next_repeat_id++;
1954 // }
1955
1956 XEvent *BC_WindowBase::new_xevent()
1957 {
1958         XEvent *event = new XEvent;
1959         memset(event, 0, sizeof(*event));
1960         return event;
1961 }
1962
1963 #ifndef SINGLE_THREAD
1964 int BC_WindowBase::arm_repeat(int64_t duration)
1965 {
1966         XEvent *event = new_xevent();
1967         XClientMessageEvent *ptr = (XClientMessageEvent*)event;
1968         ptr->type = ClientMessage;
1969         ptr->message_type = RepeaterXAtom;
1970         ptr->format = 32;
1971         ptr->data.l[0] = duration;
1972
1973 // Couldn't use XSendEvent since it locked up randomly.
1974         put_event(event);
1975         return 0;
1976 }
1977 #endif
1978
1979 int BC_WindowBase::receive_custom_xatoms(xatom_event *event)
1980 {
1981         return 0;
1982 }
1983
1984 int BC_WindowBase::send_custom_xatom(xatom_event *event)
1985 {
1986 #ifndef SINGLE_THREAD
1987         XEvent *myevent = new_xevent();
1988         XClientMessageEvent *ptr = (XClientMessageEvent*)myevent;
1989         ptr->type = ClientMessage;
1990         ptr->message_type = event->message_type;
1991         ptr->format = event->format;
1992         ptr->data.l[0] = event->data.l[0];
1993         ptr->data.l[1] = event->data.l[1];
1994         ptr->data.l[2] = event->data.l[2];
1995         ptr->data.l[3] = event->data.l[3];
1996         ptr->data.l[4] = event->data.l[4];
1997
1998         put_event(myevent);
1999 #endif
2000         return 0;
2001 }
2002
2003
2004
2005 Atom BC_WindowBase::create_xatom(const char *atom_name)
2006 {
2007         return XInternAtom(display, atom_name, False);
2008 }
2009
2010 int BC_WindowBase::get_atoms()
2011 {
2012         SetDoneXAtom =  XInternAtom(display, "BC_REPEAT_EVENT", False);
2013         RepeaterXAtom = XInternAtom(display, "BC_CLOSE_EVENT", False);
2014         DestroyAtom =   XInternAtom(display, "BC_DESTROY_WINDOW", False);
2015         DelWinXAtom =   XInternAtom(display, "WM_DELETE_WINDOW", False);
2016         if( (ProtoXAtom = XInternAtom(display, "WM_PROTOCOLS", False)) != 0 )
2017                 XChangeProperty(display, win, ProtoXAtom, XA_ATOM, 32,
2018                         PropModeReplace, (unsigned char *)&DelWinXAtom, True);
2019         return 0;
2020
2021 }
2022
2023
2024 void BC_WindowBase::init_cursors()
2025 {
2026         arrow_cursor = XCreateFontCursor(display, XC_top_left_arrow);
2027         cross_cursor = XCreateFontCursor(display, XC_crosshair);
2028         ibeam_cursor = XCreateFontCursor(display, XC_xterm);
2029         vseparate_cursor = XCreateFontCursor(display, XC_sb_v_double_arrow);
2030         hseparate_cursor = XCreateFontCursor(display, XC_sb_h_double_arrow);
2031         move_cursor = XCreateFontCursor(display, XC_fleur);
2032         left_cursor = XCreateFontCursor(display, XC_sb_left_arrow);
2033         right_cursor = XCreateFontCursor(display, XC_sb_right_arrow);
2034         upright_arrow_cursor = XCreateFontCursor(display, XC_arrow);
2035         upleft_resize_cursor = XCreateFontCursor(display, XC_top_left_corner);
2036         upright_resize_cursor = XCreateFontCursor(display, XC_top_right_corner);
2037         downleft_resize_cursor = XCreateFontCursor(display, XC_bottom_left_corner);
2038         downright_resize_cursor = XCreateFontCursor(display, XC_bottom_right_corner);
2039         hourglass_cursor = XCreateFontCursor(display, XC_watch);
2040         grabbed_cursor = create_grab_cursor();
2041
2042         static char cursor_data[] = { 0,0,0,0, 0,0,0,0 };
2043         Colormap colormap = DefaultColormap(display, screen);
2044         Pixmap pixmap_bottom = XCreateBitmapFromData(display,
2045                 rootwin, cursor_data, 8, 8);
2046         XColor black, dummy;
2047         XAllocNamedColor(display, colormap, "black", &black, &dummy);
2048         transparent_cursor = XCreatePixmapCursor(display,
2049                 pixmap_bottom, pixmap_bottom, &black, &black, 0, 0);
2050 //      XDefineCursor(display, win, transparent_cursor);
2051         XFreePixmap(display, pixmap_bottom);
2052 }
2053
2054 int BC_WindowBase::evaluate_color_model(int client_byte_order, int server_byte_order, int depth)
2055 {
2056         int color_model = BC_TRANSPARENCY;
2057         switch(depth)
2058         {
2059                 case 8:
2060                         color_model = BC_RGB8;
2061                         break;
2062                 case 16:
2063                         color_model = (server_byte_order == client_byte_order) ? BC_RGB565 : BC_BGR565;
2064                         break;
2065                 case 24:
2066                         color_model = server_byte_order ? BC_BGR888 : BC_RGB888;
2067                         break;
2068                 case 32:
2069                         color_model = server_byte_order ? BC_BGR8888 : BC_ARGB8888;
2070                         break;
2071         }
2072         return color_model;
2073 }
2074
2075 int BC_WindowBase::init_colors()
2076 {
2077         total_colors = 0;
2078         current_color_value = current_color_pixel = 0;
2079
2080 // Get the real depth
2081         char *data = 0;
2082         XImage *ximage;
2083         ximage = XCreateImage(top_level->display,
2084                                         top_level->vis,
2085                                         top_level->default_depth,
2086                                         ZPixmap,
2087                                         0,
2088                                         data,
2089                                         16,
2090                                         16,
2091                                         8,
2092                                         0);
2093         bits_per_pixel = ximage->bits_per_pixel;
2094         XDestroyImage(ximage);
2095
2096         color_model = evaluate_color_model(client_byte_order,
2097                 server_byte_order,
2098                 bits_per_pixel);
2099 // Get the color model
2100         switch(color_model)
2101         {
2102                 case BC_RGB8:
2103                         if(private_color) {
2104                                 cmap = XCreateColormap(display, rootwin, vis, AllocNone);
2105                                 create_private_colors();
2106                         }
2107                         else {
2108                                 cmap = DefaultColormap(display, screen);
2109                                 create_shared_colors();
2110                         }
2111
2112                         allocate_color_table();
2113                         break;
2114
2115                 default:
2116                         //cmap = DefaultColormap(display, screen);
2117                         cmap = XCreateColormap(display, rootwin, vis, AllocNone );
2118                         break;
2119         }
2120         return 0;
2121 }
2122
2123 int BC_WindowBase::create_private_colors()
2124 {
2125         int color;
2126         total_colors = 256;
2127
2128         for(int i = 0; i < 255; i++)
2129         {
2130                 color = (i & 0xc0) << 16;
2131                 color += (i & 0x38) << 10;
2132                 color += (i & 0x7) << 5;
2133                 color_table[i][0] = color;
2134         }
2135         create_shared_colors();        // overwrite the necessary colors on the table
2136         return 0;
2137 }
2138
2139
2140 int BC_WindowBase::create_color(int color)
2141 {
2142         if(total_colors == 256)
2143         {
2144 // replace the closest match with an exact match
2145                 color_table[get_color_rgb8(color)][0] = color;
2146         }
2147         else
2148         {
2149 // add the color to the table
2150                 color_table[total_colors][0] = color;
2151                 total_colors++;
2152         }
2153         return 0;
2154 }
2155
2156 int BC_WindowBase::create_shared_colors()
2157 {
2158         create_color(BLACK);
2159         create_color(WHITE);
2160
2161         create_color(LTGREY);
2162         create_color(MEGREY);
2163         create_color(MDGREY);
2164         create_color(DKGREY);
2165
2166         create_color(LTCYAN);
2167         create_color(MECYAN);
2168         create_color(MDCYAN);
2169         create_color(DKCYAN);
2170
2171         create_color(LTGREEN);
2172         create_color(GREEN);
2173         create_color(DKGREEN);
2174
2175         create_color(LTPINK);
2176         create_color(PINK);
2177         create_color(RED);
2178
2179         create_color(LTBLUE);
2180         create_color(BLUE);
2181         create_color(DKBLUE);
2182
2183         create_color(LTYELLOW);
2184         create_color(MEYELLOW);
2185         create_color(MDYELLOW);
2186         create_color(DKYELLOW);
2187
2188         create_color(LTPURPLE);
2189         create_color(MEPURPLE);
2190         create_color(MDPURPLE);
2191         create_color(DKPURPLE);
2192
2193         create_color(FGGREY);
2194         create_color(MNBLUE);
2195         create_color(ORANGE);
2196         create_color(FTGREY);
2197
2198         return 0;
2199 }
2200
2201 Cursor BC_WindowBase::create_grab_cursor()
2202 {
2203         int iw = 23, iw1 = iw-1, iw2 = iw/2;
2204         int ih = 23, ih1 = ih-1, ih2 = ih/2;
2205         VFrame grab(iw,ih,BC_RGB888);
2206         grab.clear_frame();
2207         grab.set_pixel_color(RED);   // fg
2208         grab.draw_smooth(iw2,0,   iw1,0,   iw1,ih2);
2209         grab.draw_smooth(iw1,ih2, iw1,ih1, iw2,ih1);
2210         grab.draw_smooth(iw2,ih1, 0,ih1,   0,ih2);
2211         grab.draw_smooth(0,ih2,   0,0,     iw2,0);
2212         grab.set_pixel_color(WHITE); // bg
2213         grab.draw_line(0,ih2,     iw2-2,ih2);
2214         grab.draw_line(iw2+2,ih2, iw1,ih2);
2215         grab.draw_line(iw2,0,     iw2,ih2-2);
2216         grab.draw_line(iw2,ih2+2, iw2,ih1);
2217
2218         int bpl = (iw+7)/8, isz = bpl * ih;
2219         char img[isz];  memset(img, 0, isz);
2220         char msk[isz];  memset(msk, 0, isz);
2221         unsigned char **rows = grab.get_rows();
2222         for( int iy=0; iy<ih; ++iy ) {
2223                 char *op = img + iy*bpl;
2224                 char *mp = msk + iy*bpl;
2225                 unsigned char *ip = rows[iy];
2226                 for( int ix=0; ix<iw; ++ix,ip+=3 ) {
2227                         if( ip[0] ) mp[ix>>3] |= (1<<(ix&7));
2228                         if( !ip[1] ) op[ix>>3] |= (1<<(ix&7));
2229                 }
2230         }
2231         unsigned long white_pix = WhitePixel(display, screen);
2232         unsigned long black_pix = BlackPixel(display, screen);
2233         Pixmap img_xpm = XCreatePixmapFromBitmapData(display, rootwin,
2234                 img, iw,ih, white_pix,black_pix, 1);
2235         Pixmap msk_xpm = XCreatePixmapFromBitmapData(display, rootwin,
2236                 msk, iw,ih, white_pix,black_pix, 1);
2237
2238         XColor fc, bc;
2239         fc.flags = bc.flags = DoRed | DoGreen | DoBlue;
2240         fc.red = 0xffff; fc.green = fc.blue = 0;  // fg
2241         bc.red = 0xffff; bc.green = 0xffff; bc.blue = 0x0000;     // bg
2242         Cursor cursor = XCreatePixmapCursor(display, img_xpm,msk_xpm, &fc,&bc, iw2,ih2);
2243         XFreePixmap(display, img_xpm);
2244         XFreePixmap(display, msk_xpm);
2245         return cursor;
2246 }
2247
2248 int BC_WindowBase::allocate_color_table()
2249 {
2250         int red, green, blue, color;
2251         XColor col;
2252
2253         for(int i = 0; i < total_colors; i++)
2254         {
2255                 color = color_table[i][0];
2256                 red = (color & 0xFF0000) >> 16;
2257                 green = (color & 0x00FF00) >> 8;
2258                 blue = color & 0xFF;
2259
2260                 col.flags = DoRed | DoGreen | DoBlue;
2261                 col.red   = red<<8   | red;
2262                 col.green = green<<8 | green;
2263                 col.blue  = blue<<8  | blue;
2264
2265                 XAllocColor(display, cmap, &col);
2266                 color_table[i][1] = col.pixel;
2267         }
2268
2269         XInstallColormap(display, cmap);
2270         return 0;
2271 }
2272
2273 int BC_WindowBase::init_window_shape()
2274 {
2275         if(bg_pixmap && bg_pixmap->use_alpha())
2276         {
2277                 XShapeCombineMask(top_level->display,
2278                         this->win, ShapeBounding, 0, 0,
2279                         bg_pixmap->get_alpha(), ShapeSet);
2280         }
2281         return 0;
2282 }
2283
2284
2285 int BC_WindowBase::init_gc()
2286 {
2287         unsigned long gcmask;
2288         gcmask = GCFont | GCGraphicsExposures;
2289
2290         XGCValues gcvalues;
2291         gcvalues.font = mediumfont->fid;        // set the font
2292         gcvalues.graphics_exposures = 0;        // prevent expose events for every redraw
2293         gc = XCreateGC(display, rootwin, gcmask, &gcvalues);
2294
2295 // gcmask = GCCapStyle | GCJoinStyle;
2296 // XGetGCValues(display, gc, gcmask, &gcvalues);
2297 // printf("BC_WindowBase::init_gc %d %d %d\n", __LINE__, gcvalues.cap_style, gcvalues.join_style);
2298         return 0;
2299 }
2300
2301 int BC_WindowBase::init_fonts()
2302 {
2303         if( !(smallfont = XLoadQueryFont(display, _(resources.small_font))) )
2304                 if( !(smallfont = XLoadQueryFont(display, _(resources.small_font2))) )
2305                         smallfont = XLoadQueryFont(display, "fixed");
2306         if( !(mediumfont = XLoadQueryFont(display, _(resources.medium_font))) )
2307                 if( !(mediumfont = XLoadQueryFont(display, _(resources.medium_font2))) )
2308                         mediumfont = XLoadQueryFont(display, "fixed");
2309         if( !(largefont = XLoadQueryFont(display, _(resources.large_font))) )
2310                 if( !(largefont = XLoadQueryFont(display, _(resources.large_font2))) )
2311                         largefont = XLoadQueryFont(display, "fixed");
2312         if( !(bigfont = XLoadQueryFont(display, _(resources.big_font))) )
2313                 if( !(bigfont = XLoadQueryFont(display, _(resources.big_font2))) )
2314                         bigfont = XLoadQueryFont(display, "fixed");
2315
2316         if((clockfont = XLoadQueryFont(display, _(resources.clock_font))) == NULL)
2317                 if((clockfont = XLoadQueryFont(display, _(resources.clock_font2))) == NULL)
2318                         clockfont = XLoadQueryFont(display, "fixed");
2319
2320         init_xft();
2321         if(get_resources()->use_fontset)
2322         {
2323                 char **m, *d;
2324                 int n;
2325
2326 // FIXME: should check the m,d,n values
2327                 smallfontset = XCreateFontSet(display, resources.small_fontset, &m, &n, &d);
2328                 if( !smallfontset )
2329                         smallfontset = XCreateFontSet(display, "fixed,*", &m, &n, &d);
2330                 mediumfontset = XCreateFontSet(display, resources.medium_fontset, &m, &n, &d);
2331                 if( !mediumfontset )
2332                         mediumfontset = XCreateFontSet(display, "fixed,*", &m, &n, &d);
2333                 largefontset = XCreateFontSet(display, resources.large_fontset, &m, &n, &d);
2334                 if( !largefontset )
2335                         largefontset = XCreateFontSet(display, "fixed,*", &m, &n, &d);
2336                 bigfontset = XCreateFontSet(display, resources.big_fontset, &m, &n, &d);
2337                 if( !bigfontset )
2338                         bigfontset = XCreateFontSet(display, "fixed,*", &m, &n, &d);
2339                 clockfontset = XCreateFontSet(display, resources.clock_fontset, &m, &n, &d);
2340                 if( !clockfontset )
2341                         clockfontset = XCreateFontSet(display, "fixed,*", &m, &n, &d);
2342                 if(clockfontset && bigfontset && largefontset && mediumfontset && smallfontset) {
2343                         curr_fontset = mediumfontset;
2344                         get_resources()->use_fontset = 1;
2345                 }
2346                 else {
2347                         curr_fontset = 0;
2348                         get_resources()->use_fontset = 0;
2349                 }
2350         }
2351
2352         return 0;
2353 }
2354
2355 void BC_WindowBase::init_xft()
2356 {
2357 #ifdef HAVE_XFT
2358         if( !get_resources()->use_xft ) return;
2359 // apparently, xft is not reentrant, more than this is needed
2360 static Mutex xft_init_lock("BC_WindowBase::xft_init_lock", 0);
2361 xft_init_lock.lock("BC_WindowBase::init_xft");
2362         if(!(smallfont_xft =
2363                 (resources.small_font_xft[0] == '-' ?
2364                         xftFontOpenXlfd(display, screen, resources.small_font_xft) :
2365                         xftFontOpenName(display, screen, resources.small_font_xft))) )
2366                 if(!(smallfont_xft =
2367                         xftFontOpenXlfd(display, screen, resources.small_font_xft2)))
2368                         smallfont_xft = xftFontOpenXlfd(display, screen, "fixed");
2369         if(!(mediumfont_xft =
2370                 (resources.medium_font_xft[0] == '-' ?
2371                         xftFontOpenXlfd(display, screen, resources.medium_font_xft) :
2372                         xftFontOpenName(display, screen, resources.medium_font_xft))) )
2373                 if(!(mediumfont_xft =
2374                         xftFontOpenXlfd(display, screen, resources.medium_font_xft2)))
2375                         mediumfont_xft = xftFontOpenXlfd(display, screen, "fixed");
2376         if(!(largefont_xft =
2377                 (resources.large_font_xft[0] == '-' ?
2378                         xftFontOpenXlfd(display, screen, resources.large_font_xft) :
2379                         xftFontOpenName(display, screen, resources.large_font_xft))) )
2380                 if(!(largefont_xft =
2381                         xftFontOpenXlfd(display, screen, resources.large_font_xft2)))
2382                         largefont_xft = xftFontOpenXlfd(display, screen, "fixed");
2383         if(!(bigfont_xft =
2384                 (resources.big_font_xft[0] == '-' ?
2385                         xftFontOpenXlfd(display, screen, resources.big_font_xft) :
2386                         xftFontOpenName(display, screen, resources.big_font_xft))) )
2387                 if(!(bigfont_xft =
2388                         xftFontOpenXlfd(display, screen, resources.big_font_xft2)))
2389                         bigfont_xft = xftFontOpenXlfd(display, screen, "fixed");
2390         if(!(clockfont_xft =
2391                 (resources.clock_font_xft[0] == '-' ?
2392                         xftFontOpenXlfd(display, screen, resources.clock_font_xft) :
2393                         xftFontOpenName(display, screen, resources.clock_font_xft))) )
2394                 clockfont_xft = xftFontOpenXlfd(display, screen, "fixed");
2395
2396
2397         if(!(bold_smallfont_xft =
2398                 (resources.small_b_font_xft[0] == '-' ?
2399                         xftFontOpenXlfd(display, screen, resources.small_b_font_xft) :
2400                         xftFontOpenName(display, screen, resources.small_b_font_xft))) )
2401                 bold_smallfont_xft = xftFontOpenXlfd(display, screen, "fixed");
2402         if(!(bold_mediumfont_xft =
2403                 (resources.medium_b_font_xft[0] == '-' ?
2404                         xftFontOpenXlfd(display, screen, resources.medium_b_font_xft) :
2405                         xftFontOpenName(display, screen, resources.medium_b_font_xft))) )
2406                 bold_mediumfont_xft = xftFontOpenXlfd(display, screen, "fixed");
2407         if(!(bold_largefont_xft =
2408                 (resources.large_b_font_xft[0] == '-' ?
2409                         xftFontOpenXlfd(display, screen, resources.large_b_font_xft) :
2410                         xftFontOpenName(display, screen, resources.large_b_font_xft))) )
2411                 bold_largefont_xft = xftFontOpenXlfd(display, screen, "fixed");
2412
2413         if( !smallfont_xft || !mediumfont_xft || !largefont_xft || !bigfont_xft ||
2414             !bold_largefont_xft || !bold_mediumfont_xft || !bold_largefont_xft ||
2415             !clockfont_xft ) {
2416                 printf("BC_WindowBase::init_fonts: no xft fonts found:"
2417                         " %s=%p\n %s=%p\n %s=%p\n %s=%p\n %s=%p\n %s=%p\n %s=%p\n %s=%p\n",
2418                         resources.small_font_xft, smallfont_xft,
2419                         resources.medium_font_xft, mediumfont_xft,
2420                         resources.large_font_xft, largefont_xft,
2421                         resources.big_font_xft, bigfont_xft,
2422                         resources.clock_font_xft, clockfont_xft,
2423                         resources.small_b_font_xft, bold_smallfont_xft,
2424                         resources.medium_b_font_xft, bold_mediumfont_xft,
2425                         resources.large_b_font_xft, bold_largefont_xft);
2426                 get_resources()->use_xft = 0;
2427                 exit(1);
2428         }
2429 // _XftDisplayInfo needs a lock.
2430         xftDefaultHasRender(display);
2431 xft_init_lock.unlock();
2432 #endif // HAVE_XFT
2433 }
2434
2435 void BC_WindowBase::init_im()
2436 {
2437         XIMStyles *xim_styles;
2438         XIMStyle xim_style;
2439
2440         if(!(input_method = XOpenIM(display, NULL, NULL, NULL)))
2441         {
2442                 printf("BC_WindowBase::init_im: Could not open input method.\n");
2443                 exit(1);
2444         }
2445         if(XGetIMValues(input_method, XNQueryInputStyle, &xim_styles, NULL) ||
2446                         xim_styles == NULL)
2447         {
2448                 printf("BC_WindowBase::init_im: Input method doesn't support any styles.\n");
2449                 XCloseIM(input_method);
2450                 exit(1);
2451         }
2452
2453         xim_style = 0;
2454         for(int z = 0;  z < xim_styles->count_styles;  z++)
2455         {
2456                 if(xim_styles->supported_styles[z] == (XIMPreeditNothing | XIMStatusNothing))
2457                 {
2458                         xim_style = xim_styles->supported_styles[z];
2459                         break;
2460                 }
2461         }
2462         XFree(xim_styles);
2463
2464         if(xim_style == 0)
2465         {
2466                 printf("BC_WindowBase::init_im: Input method doesn't support the style we need.\n");
2467                 XCloseIM(input_method);
2468                 exit(1);
2469         }
2470
2471         input_context = XCreateIC(input_method, XNInputStyle, xim_style,
2472                 XNClientWindow, win, XNFocusWindow, win, NULL);
2473         if(!input_context)
2474         {
2475                 printf("BC_WindowBase::init_im: Failed to create input context.\n");
2476                 XCloseIM(input_method);
2477                 exit(1);
2478         }
2479 }
2480
2481 void BC_WindowBase::finit_im()
2482 {
2483         if( input_context ) {
2484                 XDestroyIC(input_context);
2485                 input_context = 0;
2486         }
2487         if( input_method ) {
2488                 XCloseIM(input_method);
2489                 input_method = 0;
2490         }
2491 }
2492
2493
2494 int BC_WindowBase::get_color(int64_t color)
2495 {
2496 // return pixel of color
2497 // use this only for drawing subwindows not for bitmaps
2498          int i, test, difference;
2499
2500         switch(color_model)
2501         {
2502         case BC_RGB8:
2503                 if(private_color)
2504                         return get_color_rgb8(color);
2505 // test last color looked up
2506                 if(current_color_value == color)
2507                         return current_color_pixel;
2508
2509 // look up in table
2510                 current_color_value = color;
2511                 for(i = 0; i < total_colors; i++)
2512                 {
2513                         if(color_table[i][0] == color)
2514                         {
2515                                 current_color_pixel = color_table[i][1];
2516                                 return current_color_pixel;
2517                         }
2518                 }
2519
2520 // find nearest match
2521                 difference = 0xFFFFFF;
2522
2523                 for(i = 0; i < total_colors; i++)
2524                 {
2525                         test = abs((int)(color_table[i][0] - color));
2526
2527                         if(test < difference)
2528                         {
2529                                 current_color_pixel = color_table[i][1];
2530                                 difference = test;
2531                         }
2532                 }
2533                 return current_color_pixel;
2534
2535         case BC_RGB565:
2536                 return get_color_rgb16(color);
2537
2538         case BC_BGR565:
2539                 return get_color_bgr16(color);
2540
2541         case BC_RGB888:
2542         case BC_BGR888:
2543                 return client_byte_order == server_byte_order ?
2544                         color : get_color_bgr24(color);
2545
2546         default:
2547                 return color;
2548         }
2549         return 0;
2550 }
2551
2552 int BC_WindowBase::get_color_rgb8(int color)
2553 {
2554         int pixel;
2555
2556         pixel = (color & 0xc00000) >> 16;
2557         pixel += (color & 0xe000) >> 10;
2558         pixel += (color & 0xe0) >> 5;
2559         return pixel;
2560 }
2561
2562 int64_t BC_WindowBase::get_color_rgb16(int color)
2563 {
2564         int64_t result;
2565         result = (color & 0xf80000) >> 8;
2566         result += (color & 0xfc00) >> 5;
2567         result += (color & 0xf8) >> 3;
2568
2569         return result;
2570 }
2571
2572 int64_t BC_WindowBase::get_color_bgr16(int color)
2573 {
2574         int64_t result;
2575         result = (color & 0xf80000) >> 19;
2576         result += (color & 0xfc00) >> 5;
2577         result += (color & 0xf8) << 8;
2578
2579         return result;
2580 }
2581
2582 int64_t BC_WindowBase::get_color_bgr24(int color)
2583 {
2584         int64_t result;
2585         result = (color & 0xff) << 16;
2586         result += (color & 0xff00);
2587         result += (color & 0xff0000) >> 16;
2588         return result;
2589 }
2590
2591 void BC_WindowBase::start_video()
2592 {
2593         cursor_timer->update();
2594         video_on = 1;
2595 //      set_color(BLACK);
2596 //      draw_box(0, 0, get_w(), get_h());
2597 //      flash();
2598 }
2599
2600 void BC_WindowBase::stop_video()
2601 {
2602         video_on = 0;
2603         unhide_cursor();
2604 }
2605
2606
2607
2608 int64_t BC_WindowBase::get_color()
2609 {
2610         return top_level->current_color;
2611 }
2612
2613 void BC_WindowBase::set_color(int64_t color)
2614 {
2615         top_level->current_color = color;
2616         XSetForeground(top_level->display,
2617                 top_level->gc,
2618                 top_level->get_color(color));
2619 }
2620
2621 void BC_WindowBase::set_opaque()
2622 {
2623         XSetFunction(top_level->display, top_level->gc, GXcopy);
2624 }
2625
2626 void BC_WindowBase::set_inverse()
2627 {
2628         XSetFunction(top_level->display, top_level->gc, GXxor);
2629 }
2630
2631 void BC_WindowBase::set_line_width(int value)
2632 {
2633         this->line_width = value;
2634         XSetLineAttributes(top_level->display, top_level->gc, value,    /* line_width */
2635                 line_dashes == 0 ? LineSolid : LineOnOffDash,           /* line_style */
2636                 line_dashes == 0 ? CapRound : CapNotLast,               /* cap_style */
2637                 JoinMiter);                                             /* join_style */
2638
2639         if(line_dashes > 0) {
2640                 const char dashes = line_dashes;
2641                 XSetDashes(top_level->display, top_level->gc, 0, &dashes, 1);
2642         }
2643
2644 // XGCValues gcvalues;
2645 // unsigned long gcmask;
2646 // gcmask = GCCapStyle | GCJoinStyle;
2647 // XGetGCValues(top_level->display, top_level->gc, gcmask, &gcvalues);
2648 // printf("BC_WindowBase::set_line_width %d %d %d\n", __LINE__, gcvalues.cap_style, gcvalues.join_style);
2649 }
2650
2651 void BC_WindowBase::set_line_dashes(int value)
2652 {
2653         line_dashes = value;
2654 // call XSetLineAttributes
2655         set_line_width(line_width);
2656 }
2657
2658
2659 Cursor BC_WindowBase::get_cursor_struct(int cursor)
2660 {
2661         switch(cursor)
2662         {
2663                 case ARROW_CURSOR:         return top_level->arrow_cursor;
2664                 case CROSS_CURSOR:         return top_level->cross_cursor;
2665                 case IBEAM_CURSOR:         return top_level->ibeam_cursor;
2666                 case VSEPARATE_CURSOR:     return top_level->vseparate_cursor;
2667                 case HSEPARATE_CURSOR:     return top_level->hseparate_cursor;
2668                 case MOVE_CURSOR:          return top_level->move_cursor;
2669                 case LEFT_CURSOR:          return top_level->left_cursor;
2670                 case RIGHT_CURSOR:         return top_level->right_cursor;
2671                 case UPRIGHT_ARROW_CURSOR: return top_level->upright_arrow_cursor;
2672                 case UPLEFT_RESIZE:        return top_level->upleft_resize_cursor;
2673                 case UPRIGHT_RESIZE:       return top_level->upright_resize_cursor;
2674                 case DOWNLEFT_RESIZE:      return top_level->downleft_resize_cursor;
2675                 case DOWNRIGHT_RESIZE:     return top_level->downright_resize_cursor;
2676                 case HOURGLASS_CURSOR:     return top_level->hourglass_cursor;
2677                 case TRANSPARENT_CURSOR:   return top_level->transparent_cursor;
2678                 case GRABBED_CURSOR:       return top_level->grabbed_cursor;
2679         }
2680         return 0;
2681 }
2682
2683 void BC_WindowBase::set_cursor(int cursor, int override, int flush)
2684 {
2685 // inherit cursor from parent
2686         if(cursor < 0)
2687         {
2688                 XUndefineCursor(top_level->display, win);
2689                 current_cursor = cursor;
2690         }
2691         else
2692 // don't change cursor if overridden
2693         if((!top_level->is_hourglass && !is_transparent) ||
2694                 override)
2695         {
2696                 XDefineCursor(top_level->display, win, get_cursor_struct(cursor));
2697                 if(flush) this->flush();
2698         }
2699
2700         if(!override) current_cursor = cursor;
2701 }
2702
2703 void BC_WindowBase::set_x_cursor(int cursor)
2704 {
2705         temp_cursor = XCreateFontCursor(top_level->display, cursor);
2706         XDefineCursor(top_level->display, win, temp_cursor);
2707         current_cursor = cursor;
2708         flush();
2709 }
2710
2711 int BC_WindowBase::get_cursor()
2712 {
2713         return current_cursor;
2714 }
2715
2716 void BC_WindowBase::start_hourglass()
2717 {
2718         top_level->start_hourglass_recursive();
2719         top_level->flush();
2720 }
2721
2722 void BC_WindowBase::stop_hourglass()
2723 {
2724         top_level->stop_hourglass_recursive();
2725         top_level->flush();
2726 }
2727
2728 void BC_WindowBase::start_hourglass_recursive()
2729 {
2730         if(this == top_level)
2731         {
2732                 hourglass_total++;
2733                 is_hourglass = 1;
2734         }
2735
2736         if(!is_transparent)
2737         {
2738                 set_cursor(HOURGLASS_CURSOR, 1, 0);
2739                 for(int i = 0; i < subwindows->total; i++)
2740                 {
2741                         subwindows->values[i]->start_hourglass_recursive();
2742                 }
2743         }
2744 }
2745
2746 void BC_WindowBase::stop_hourglass_recursive()
2747 {
2748         if(this == top_level)
2749         {
2750                 if(hourglass_total == 0) return;
2751                 top_level->hourglass_total--;
2752         }
2753
2754         if(!top_level->hourglass_total)
2755         {
2756                 top_level->is_hourglass = 0;
2757
2758 // Cause set_cursor to perform change
2759                 if(!is_transparent)
2760                         set_cursor(current_cursor, 1, 0);
2761
2762                 for(int i = 0; i < subwindows->total; i++)
2763                 {
2764                         subwindows->values[i]->stop_hourglass_recursive();
2765                 }
2766         }
2767 }
2768
2769
2770
2771
2772 XFontStruct* BC_WindowBase::get_font_struct(int font)
2773 {
2774 // Clear out unrelated flags
2775         if(font & BOLDFACE) font ^= BOLDFACE;
2776
2777         switch(font) {
2778                 case SMALLFONT:  return top_level->smallfont;  break;
2779                 case MEDIUMFONT: return top_level->mediumfont; break;
2780                 case LARGEFONT:  return top_level->largefont;  break;
2781                 case BIGFONT:    return top_level->bigfont;    break;
2782                 case CLOCKFONT:  return top_level->clockfont;  break;
2783         }
2784         return 0;
2785 }
2786
2787 XFontSet BC_WindowBase::get_fontset(int font)
2788 {
2789         XFontSet fs = 0;
2790
2791         if(get_resources()->use_fontset)
2792         {
2793                 switch(font & 0xff) {
2794                         case SMALLFONT:  fs = top_level->smallfontset; break;
2795                         case MEDIUMFONT: fs = top_level->mediumfontset; break;
2796                         case LARGEFONT:  fs = top_level->largefontset; break;
2797                         case BIGFONT:    fs = top_level->bigfontset;   break;
2798                         case CLOCKFONT: fs = top_level->clockfontset; break;
2799                 }
2800         }
2801
2802         return fs;
2803 }
2804
2805 #ifdef HAVE_XFT
2806 XftFont* BC_WindowBase::get_xft_struct(int font)
2807 {
2808         switch(font) {
2809                 case SMALLFONT:    return (XftFont*)top_level->smallfont_xft;
2810                 case MEDIUMFONT:   return (XftFont*)top_level->mediumfont_xft;
2811                 case LARGEFONT:    return (XftFont*)top_level->largefont_xft;
2812                 case BIGFONT:      return (XftFont*)top_level->bigfont_xft;
2813                 case CLOCKFONT:    return (XftFont*)top_level->clockfont_xft;
2814                 case MEDIUMFONT_3D: return (XftFont*)top_level->bold_mediumfont_xft;
2815                 case SMALLFONT_3D:  return (XftFont*)top_level->bold_smallfont_xft;
2816                 case LARGEFONT_3D:  return (XftFont*)top_level->bold_largefont_xft;
2817         }
2818
2819         return 0;
2820 }
2821 #endif
2822
2823
2824 int BC_WindowBase::get_current_font()
2825 {
2826         return top_level->current_font;
2827 }
2828
2829 void BC_WindowBase::set_font(int font)
2830 {
2831         top_level->current_font = font;
2832
2833 #ifdef HAVE_XFT
2834         if(get_resources()->use_xft) {}
2835         else
2836 #endif
2837         if(get_resources()->use_fontset) {
2838                 set_fontset(font);
2839         }
2840
2841         if(get_font_struct(font))
2842         {
2843                 XSetFont(top_level->display, top_level->gc, get_font_struct(font)->fid);
2844         }
2845
2846         return;
2847 }
2848
2849 void BC_WindowBase::set_fontset(int font)
2850 {
2851         XFontSet fs = 0;
2852
2853         if(get_resources()->use_fontset) {
2854                 switch(font) {
2855                         case SMALLFONT:  fs = top_level->smallfontset; break;
2856                         case MEDIUMFONT: fs = top_level->mediumfontset; break;
2857                         case LARGEFONT:  fs = top_level->largefontset; break;
2858                         case BIGFONT:    fs = top_level->bigfontset;   break;
2859                         case CLOCKFONT:  fs = top_level->clockfontset; break;
2860                 }
2861         }
2862
2863         curr_fontset = fs;
2864 }
2865
2866
2867 XFontSet BC_WindowBase::get_curr_fontset(void)
2868 {
2869         if(get_resources()->use_fontset)
2870                 return curr_fontset;
2871         return 0;
2872 }
2873
2874 int BC_WindowBase::get_single_text_width(int font, const char *text, int length)
2875 {
2876 #ifdef HAVE_XFT
2877         if(get_resources()->use_xft && get_xft_struct(font))
2878         {
2879                 XGlyphInfo extents;
2880 #ifdef X_HAVE_UTF8_STRING
2881                 if(get_resources()->locale_utf8)
2882                 {
2883                         xftTextExtentsUtf8(top_level->display,
2884                                 get_xft_struct(font),
2885                                 (const XftChar8 *)text,
2886                                 length,
2887                                 &extents);
2888                 }
2889                 else
2890 #endif
2891                 {
2892                         xftTextExtents8(top_level->display,
2893                                 get_xft_struct(font),
2894                                 (const XftChar8 *)text,
2895                                 length,
2896                                 &extents);
2897                 }
2898                 return extents.xOff;
2899         }
2900         else
2901 #endif
2902         if(get_resources()->use_fontset && top_level->get_fontset(font))
2903                 return XmbTextEscapement(top_level->get_fontset(font), text, length);
2904         else
2905         if(get_font_struct(font))
2906                 return XTextWidth(get_font_struct(font), text, length);
2907         else
2908         {
2909                 int w = 0;
2910                 switch(font)
2911                 {
2912                         case MEDIUM_7SEGMENT:
2913                                 return get_resources()->medium_7segment[0]->get_w() * length;
2914                                 break;
2915
2916                         default:
2917                                 return 0;
2918                 }
2919                 return w;
2920         }
2921 }
2922
2923 int BC_WindowBase::get_text_width(int font, const char *text, int length)
2924 {
2925         int i, j, w = 0, line_w = 0;
2926         if(length < 0) length = strlen(text);
2927
2928         for(i = 0, j = 0; i <= length; i++)
2929         {
2930                 line_w = 0;
2931                 if(text[i] == '\n')
2932                 {
2933                         line_w = get_single_text_width(font, &text[j], i - j);
2934                         j = i + 1;
2935                 }
2936                 else
2937                 if(text[i] == 0)
2938                 {
2939                         line_w = get_single_text_width(font, &text[j], length - j);
2940                 }
2941                 if(line_w > w) w = line_w;
2942         }
2943
2944         if(i > length && w == 0)
2945         {
2946                 w = get_single_text_width(font, text, length);
2947         }
2948
2949         return w;
2950 }
2951
2952 int BC_WindowBase::get_text_width(int font, const wchar_t *text, int length)
2953 {
2954         int i, j, w = 0;
2955         if( length < 0 ) length = wcslen(text);
2956
2957         for( i=j=0; i<length && text[i]; ++i ) {
2958                 if( text[i] != '\n' ) continue;
2959                 if( i > j ) {
2960                         int lw = get_single_text_width(font, &text[j], i-j);
2961                         if( w < lw ) w = lw;
2962                 }
2963                 j = i + 1;
2964         }
2965         if( j < length ) {
2966                 int lw = get_single_text_width(font, &text[j], length-j);
2967                 if( w < lw ) w = lw;
2968         }
2969
2970         return w;
2971 }
2972
2973 int BC_WindowBase::get_text_ascent(int font)
2974 {
2975 #ifdef HAVE_XFT
2976         XftFont *fstruct;
2977         if( (fstruct = get_xft_struct(font)) != 0 )
2978                 return fstruct->ascent;
2979 #endif
2980         if(get_resources()->use_fontset && top_level->get_fontset(font))
2981         {
2982                 XFontSetExtents *extents;
2983
2984                 extents = XExtentsOfFontSet(top_level->get_fontset(font));
2985                 return -extents->max_logical_extent.y;
2986         }
2987
2988         if(get_font_struct(font))
2989                 return top_level->get_font_struct(font)->ascent;
2990
2991         switch(font) {
2992                 case MEDIUM_7SEGMENT:
2993                         return get_resources()->medium_7segment[0]->get_h();
2994         }
2995         return 0;
2996 }
2997
2998 int BC_WindowBase::get_text_descent(int font)
2999 {
3000 #ifdef HAVE_XFT
3001         XftFont *fstruct;
3002         if( (fstruct = get_xft_struct(font)) != 0 )
3003                 return fstruct->descent;
3004 #endif
3005         if(get_resources()->use_fontset && top_level->get_fontset(font)) {
3006                 XFontSetExtents *extents;
3007                 extents = XExtentsOfFontSet(top_level->get_fontset(font));
3008                 return (extents->max_logical_extent.height
3009                         + extents->max_logical_extent.y);
3010         }
3011
3012         if(get_font_struct(font))
3013                 return top_level->get_font_struct(font)->descent;
3014
3015         return 0;
3016 }
3017
3018 int BC_WindowBase::get_text_height(int font, const char *text)
3019 {
3020         int rowh;
3021 #ifdef HAVE_XFT
3022         XftFont *fstruct;
3023         if( (fstruct = get_xft_struct(font)) != 0 )
3024                 rowh = fstruct->height;
3025         else
3026 #endif
3027                 rowh = get_text_ascent(font) + get_text_descent(font);
3028
3029         if(!text) return rowh;
3030
3031 // Add height of lines
3032         int h = 0, i, length = strlen(text);
3033         for(i = 0; i <= length; i++)
3034         {
3035                 if(text[i] == '\n')
3036                         h++;
3037                 else
3038                 if(text[i] == 0)
3039                         h++;
3040         }
3041         return h * rowh;
3042 }
3043
3044 BC_Bitmap* BC_WindowBase::new_bitmap(int w, int h, int color_model)
3045 {
3046         if(color_model < 0) color_model = top_level->get_color_model();
3047         return new BC_Bitmap(top_level, w, h, color_model);
3048 }
3049
3050 void BC_WindowBase::init_wait()
3051 {
3052 #ifndef SINGLE_THREAD
3053         if(window_type != MAIN_WINDOW)
3054                 top_level->init_wait();
3055         init_lock->lock("BC_WindowBase::init_wait");
3056         init_lock->unlock();
3057 #endif
3058 }
3059
3060 int BC_WindowBase::accel_available(int color_model, int lock_it)
3061 {
3062         if( window_type != MAIN_WINDOW )
3063                 return top_level->accel_available(color_model, lock_it);
3064         if( lock_it )
3065                 lock_window("BC_WindowBase::accel_available");
3066
3067         switch(color_model) {
3068         case BC_YUV420P:
3069                 grab_port_id(color_model);
3070                 break;
3071
3072         case BC_YUV422:
3073                 grab_port_id(color_model);
3074                 break;
3075
3076         default:
3077                 break;
3078         }
3079
3080         if( lock_it )
3081                 unlock_window();
3082 //printf("BC_WindowBase::accel_available %d %d\n", color_model, xvideo_port_id);
3083         return xvideo_port_id >= 0 ? 1 : 0;
3084 }
3085
3086
3087 int BC_WindowBase::grab_port_id(int color_model)
3088 {
3089         if( !get_resources()->use_xvideo ||     // disabled
3090             !get_resources()->use_shm )         // Only local server is fast enough.
3091                 return -1;
3092         if( xvideo_port_id >= 0 )
3093                 return xvideo_port_id;
3094
3095         unsigned int ver, rev, reqBase, eventBase, errorBase;
3096         if( Success != XvQueryExtension(display, // XV extension is available
3097                     &ver, &rev, &reqBase, &eventBase, &errorBase) )
3098                 return -1;
3099
3100 // XV adaptors are available
3101         unsigned int numAdapt = 0;
3102         XvAdaptorInfo *info = 0;
3103         XvQueryAdaptors(display, DefaultRootWindow(display), &numAdapt, &info);
3104         if( !numAdapt )
3105                 return -1;
3106
3107 // Translate from color_model to X color model
3108         int x_color_model = BC_CModels::bc_to_x(color_model);
3109
3110 // Get adaptor with desired color model
3111         for( int i = 0; i < (int)numAdapt && xvideo_port_id == -1; i++) {
3112                 if( !(info[i].type & XvImageMask) || !info[i].num_ports ) continue;
3113 // adaptor supports XvImages
3114                 int numFormats = 0, numPorts = info[i].num_ports;
3115                 XvImageFormatValues *formats =
3116                         XvListImageFormats(display, info[i].base_id, &numFormats);
3117                 if( !formats ) continue;
3118
3119                 for( int j=0; j<numFormats && xvideo_port_id<0; ++j ) {
3120                         if( formats[j].id != x_color_model ) continue;
3121 // this adaptor supports the desired format, grab a port
3122                         for( int k=0; k<numPorts; ++k ) {
3123                                 if( Success == XvGrabPort(top_level->display,
3124                                         info[i].base_id+k, CurrentTime) ) {
3125 //printf("BC_WindowBase::grab_port_id %llx\n", info[i].base_id);
3126                                         xvideo_port_id = info[i].base_id + k;
3127                                         break;
3128                                 }
3129                         }
3130                 }
3131                 XFree(formats);
3132         }
3133
3134         XvFreeAdaptorInfo(info);
3135
3136         return xvideo_port_id;
3137 }
3138
3139
3140 int BC_WindowBase::show_window(int flush)
3141 {
3142         for(int i = 0; i < subwindows->size(); i++)
3143         {
3144                 subwindows->get(i)->show_window(0);
3145         }
3146
3147         XMapWindow(top_level->display, win);
3148         if(flush) XFlush(top_level->display);
3149 //      XSync(top_level->display, 0);
3150         hidden = 0;
3151         return 0;
3152 }
3153
3154 int BC_WindowBase::hide_window(int flush)
3155 {
3156         for(int i = 0; i < subwindows->size(); i++)
3157         {
3158                 subwindows->get(i)->hide_window(0);
3159         }
3160
3161         XUnmapWindow(top_level->display, win);
3162         if(flush) this->flush();
3163         hidden = 1;
3164         return 0;
3165 }
3166
3167 BC_MenuBar* BC_WindowBase::add_menubar(BC_MenuBar *menu_bar)
3168 {
3169         subwindows->append((BC_SubWindow*)menu_bar);
3170
3171         menu_bar->parent_window = this;
3172         menu_bar->top_level = this->top_level;
3173         menu_bar->initialize();
3174         return menu_bar;
3175 }
3176
3177 BC_WindowBase* BC_WindowBase::add_popup(BC_WindowBase *window)
3178 {
3179 //printf("BC_WindowBase::add_popup window=%p win=%p\n", window, window->win);
3180         if(this != top_level) return top_level->add_popup(window);
3181         popups.append(window);
3182         return window;
3183 }
3184
3185 void BC_WindowBase::remove_popup(BC_WindowBase *window)
3186 {
3187 //printf("BC_WindowBase::remove_popup %d size=%d window=%p win=%p\n", __LINE__, popups.size(), window, window->win);
3188         if(this != top_level)
3189                 top_level->remove_popup(window);
3190         else
3191                 popups.remove(window);
3192 //printf("BC_WindowBase::remove_popup %d size=%d window=%p win=%p\n", __LINE__, popups.size(), window, window->win);
3193 }
3194
3195
3196 BC_WindowBase* BC_WindowBase::add_subwindow(BC_WindowBase *subwindow)
3197 {
3198         subwindows->append(subwindow);
3199
3200         if(subwindow->bg_color == -1) subwindow->bg_color = this->bg_color;
3201
3202 // parent window must be set before the subwindow initialization
3203         subwindow->parent_window = this;
3204         subwindow->top_level = this->top_level;
3205
3206 // Execute derived initialization
3207         subwindow->initialize();
3208         return subwindow;
3209 }
3210
3211
3212 BC_WindowBase* BC_WindowBase::add_tool(BC_WindowBase *subwindow)
3213 {
3214         return add_subwindow(subwindow);
3215 }
3216
3217 int BC_WindowBase::flash(int x, int y, int w, int h, int flush)
3218 {
3219         if( !top_level->flash_enabled ) return 0;
3220 //printf("BC_WindowBase::flash %d %d %d %d %d\n", __LINE__, w, h, this->w, this->h);
3221         set_opaque();
3222         XSetWindowBackgroundPixmap(top_level->display, win, pixmap->opaque_pixmap);
3223         if(x >= 0)
3224         {
3225                 XClearArea(top_level->display, win, x, y, w, h, 0);
3226         }
3227         else
3228         {
3229                 XClearWindow(top_level->display, win);
3230         }
3231
3232         if(flush)
3233                 this->flush();
3234         return 0;
3235 }
3236
3237 int BC_WindowBase::flash(int flush)
3238 {
3239         flash(-1, -1, -1, -1, flush);
3240         return 0;
3241 }
3242
3243 void BC_WindowBase::flush()
3244 {
3245         //if(!get_window_lock())
3246         //      printf("BC_WindowBase::flush %s not locked\n", top_level->title);
3247         // X gets hosed if Flush/Sync are not user locked (at libX11-1.1.5 / libxcb-1.1.91)
3248         //   _XReply deadlocks in condition_wait waiting for xlib lock when waiters!=-1
3249         int locked  = get_window_lock();
3250         if( !locked ) lock_window("BC_WindowBase::flush");
3251         XFlush(top_level->display);
3252         if( !locked ) unlock_window();
3253 }
3254
3255 void BC_WindowBase::sync_display()
3256 {
3257         int locked  = get_window_lock();
3258         if( !locked ) lock_window("BC_WindowBase::sync_display");
3259         XSync(top_level->display, False);
3260         if( !locked ) unlock_window();
3261 }
3262
3263 int BC_WindowBase::get_window_lock()
3264 {
3265 #ifdef SINGLE_THREAD
3266         return BC_Display::display_global->get_display_locked();
3267 #else
3268         return top_level->window_lock;
3269 #endif
3270 }
3271
3272 int BC_WindowBase::lock_window(const char *location)
3273 {
3274         if(top_level && top_level != this)
3275         {
3276                 top_level->lock_window(location);
3277         }
3278         else
3279         if(top_level)
3280         {
3281                 SET_LOCK(this, title, location);
3282 #ifdef SINGLE_THREAD
3283                 BC_Display::lock_display(location);
3284 #else
3285                 XLockDisplay(top_level->display);
3286                 top_level->display_lock_owner = pthread_self();
3287 #endif
3288                 SET_LOCK2
3289                 ++top_level->window_lock;
3290         }
3291         else
3292         {
3293                 printf("BC_WindowBase::lock_window top_level NULL\n");
3294         }
3295         return 0;
3296 }
3297
3298 int BC_WindowBase::unlock_window()
3299 {
3300         if(top_level && top_level != this)
3301         {
3302                 top_level->unlock_window();
3303         }
3304         else
3305         if(top_level)
3306         {
3307                 UNSET_LOCK(this);
3308                 if( !top_level->window_lock ) {
3309                         printf("BC_WindowBase::unlock_window %s not locked\n", title);
3310                         booby();
3311                 }
3312                 if( top_level->window_lock > 0 )
3313                         if( --top_level->window_lock == 0 )
3314                                 top_level->display_lock_owner = 0;
3315 #ifdef SINGLE_THREAD
3316                 BC_Display::unlock_display();
3317 #else
3318                 XUnlockDisplay(top_level->display);
3319 #endif
3320         }
3321         else
3322         {
3323                 printf("BC_WindowBase::unlock_window top_level NULL\n");
3324         }
3325         return 0;
3326 }
3327
3328 int BC_WindowBase::break_lock()
3329 {
3330         if( !top_level ) return 0;
3331         if( top_level != this ) return top_level->break_lock();
3332         if( top_level->display_lock_owner != pthread_self() ) return 0;
3333         if( top_level->window_lock != 1 ) return 0;
3334         UNSET_LOCK(this);
3335         window_lock = 0;
3336         display_lock_owner = 0;
3337 #ifdef SINGLE_THREAD
3338         BC_Display::unlock_display();
3339 #else
3340         XUnlockDisplay(display);
3341 #endif
3342         return 1;
3343 }
3344
3345 void BC_WindowBase::set_done(int return_value)
3346 {
3347         if(done_set) return;
3348         done_set = 1;
3349         if(window_type != MAIN_WINDOW)
3350                 top_level->set_done(return_value);
3351         else
3352         {
3353 #ifdef SINGLE_THREAD
3354                 this->return_value = return_value;
3355                 BC_Display::display_global->arm_completion(this);
3356                 completion_lock->unlock();
3357 #else // SINGLE_THREAD
3358                 init_wait();
3359                 if( !event_thread ) return;
3360                 XEvent *event = new_xevent();
3361                 XClientMessageEvent *ptr = (XClientMessageEvent*)event;
3362                 event->type = ClientMessage;
3363                 ptr->message_type = SetDoneXAtom;
3364                 ptr->format = 32;
3365                 this->return_value = return_value;
3366 // May lock up here because XSendEvent doesn't work too well
3367 // asynchronous with XNextEvent.
3368 // This causes BC_WindowEvents to forward a copy of the event to run_window where
3369 // it is deleted.
3370 // Deletion of event_thread is done at the end of BC_WindowBase::run_window() - by calling the destructor
3371                 put_event(event);
3372 #endif
3373         }
3374 }
3375
3376 void BC_WindowBase::close(int return_value)
3377 {
3378         hide_window();  flush();
3379         set_done(return_value);
3380 }
3381
3382 int BC_WindowBase::grab(BC_WindowBase *window)
3383 {
3384         if( window->active_grab && this != window->active_grab ) return 0;
3385         window->active_grab = this;
3386         this->grab_active = window;
3387         return 1;
3388 }
3389 int BC_WindowBase::ungrab(BC_WindowBase *window)
3390 {
3391         if( window->active_grab && this != window->active_grab ) return 0;
3392         window->active_grab = 0;
3393         this->grab_active = 0;
3394         return 1;
3395 }
3396 int BC_WindowBase::grab_event_count()
3397 {
3398         int result = 0;
3399 #ifndef SINGLE_THREAD
3400         result = grab_active->get_event_count();
3401 #endif
3402         return result;
3403 }
3404 int BC_WindowBase::grab_buttons()
3405 {
3406         XSync(top_level->display, False);
3407         if( XGrabButton(top_level->display, AnyButton, AnyModifier,
3408                         top_level->rootwin, True, ButtonPressMask | ButtonReleaseMask,
3409                         GrabModeAsync, GrabModeSync, None, None) == GrabSuccess ) {
3410                 set_active_subwindow(this);
3411                 return 0;
3412         }
3413         return 1;
3414 }
3415 void BC_WindowBase::ungrab_buttons()
3416 {
3417         XUngrabButton(top_level->display, AnyButton, AnyModifier, top_level->rootwin);
3418         set_active_subwindow(0);
3419         unhide_cursor();
3420 }
3421 void BC_WindowBase::grab_cursor()
3422 {
3423         Cursor cursor_grab = get_cursor_struct(GRABBED_CURSOR);
3424         XGrabPointer(top_level->display, top_level->rootwin, True,
3425                 PointerMotionMask | ButtonPressMask | ButtonReleaseMask,
3426                 GrabModeAsync, GrabModeAsync, None, cursor_grab, CurrentTime);
3427 }
3428 void BC_WindowBase::ungrab_cursor()
3429 {
3430         XUngrabPointer(top_level->display, CurrentTime);
3431 }
3432
3433 // for get_root_w/h
3434 //   WidthOfScreen/HeightOfScreen of XDefaultScreenOfDisplay
3435 //   this is the bounding box of all the screens
3436
3437 int BC_WindowBase::get_root_w(int lock_display)
3438 {
3439         if(lock_display) lock_window("BC_WindowBase::get_root_w");
3440         Screen *def_screen = XDefaultScreenOfDisplay(top_level->display);
3441         int result = WidthOfScreen(def_screen);
3442         if(lock_display) unlock_window();
3443         return result;
3444 }
3445
3446 int BC_WindowBase::get_root_h(int lock_display)
3447 {
3448         if(lock_display) lock_window("BC_WindowBase::get_root_h");
3449         Screen *def_screen = XDefaultScreenOfDisplay(top_level->display);
3450         int result = HeightOfScreen(def_screen);
3451         if(lock_display) unlock_window();
3452         return result;
3453 }
3454
3455 XineramaScreenInfo *
3456 BC_WindowBase::get_xinerama_info(int screen)
3457 {
3458         if( !xinerama_info || !xinerama_screens ) return 0;
3459         if( screen >= 0 ) {
3460                 for( int i=0; i<xinerama_screens; ++i )
3461                         if( xinerama_info[i].screen_number == screen )
3462                                 return &xinerama_info[i];
3463                 return 0;
3464         }
3465         int top_x = get_x(), top_y = get_y();
3466         if(  BC_DisplayInfo::left_border >= 0 ) top_x +=  BC_DisplayInfo::left_border;
3467         if(  BC_DisplayInfo::top_border >= 0 ) top_y +=  BC_DisplayInfo::top_border;
3468         for( int i=0; i<xinerama_screens; ++i ) {
3469                 int scr_y = top_y - xinerama_info[i].y_org;
3470                 if( scr_y < 0 || scr_y >= xinerama_info[i].height ) continue;
3471                 int scr_x = top_x - xinerama_info[i].x_org;
3472                 if( scr_x >= 0 && scr_x < xinerama_info[i].width )
3473                         return &xinerama_info[i];
3474         }
3475         return 0;
3476 }
3477
3478 void BC_WindowBase::get_fullscreen_geometry(int &wx, int &wy, int &ww, int &wh)
3479 {
3480         XineramaScreenInfo *info = top_level->get_xinerama_info(-1);
3481         if( info ) {
3482                 wx = info->x_org;  wy = info->y_org;
3483                 ww = info->width;  wh = info->height;
3484         }
3485         else {
3486                 wx = get_screen_x(0, -1);
3487                 wy = get_screen_y(0, -1);
3488                 int scr_w0 = get_screen_w(0, 0);
3489                 int root_w = get_root_w(0);
3490                 int root_h = get_root_h(0);
3491                 if( root_w > scr_w0 ) { // multi-headed
3492                         if( wx >= scr_w0 ) {
3493                                 // assumes right side is the big one
3494                                 ww = root_w - scr_w0;
3495                                 wh = root_h;
3496                         }
3497                         else {
3498                                 // use same aspect ratio to compute left height
3499                                 ww = scr_w0;
3500                                 wh = (w*root_h) / (root_w-scr_w0);
3501                         }
3502                 }
3503                 else {
3504                         ww = root_w;
3505                         wh = root_h;
3506                 }
3507         }
3508 }
3509
3510 int BC_WindowBase::get_screen_x(int lock_display, int screen)
3511 {
3512         int result = -1;
3513         if(lock_display) lock_window("BC_WindowBase::get_screen_x");
3514         XineramaScreenInfo *info = top_level->get_xinerama_info(screen);
3515         if( !info ) {
3516                 result = 0;
3517                 int root_w = get_root_w(0);
3518                 int root_h = get_root_h(0);
3519 // Shift X based on position of current window if dual head
3520                 if( (float)root_w/root_h > 1.8 ) {
3521                         root_w = get_screen_w(0, 0);
3522                         if( top_level->get_x() >= root_w )
3523                                 result = root_w;
3524                 }
3525         }
3526         else
3527                 result = info->x_org;
3528         if(lock_display) unlock_window();
3529         return result;
3530 }
3531
3532 int BC_WindowBase::get_screen_y(int lock_display, int screen)
3533 {
3534         if(lock_display) lock_window("BC_WindowBase::get_screen_y");
3535         XineramaScreenInfo *info = top_level->get_xinerama_info(screen);
3536         int result = !info ? 0 : info->y_org;
3537         if(lock_display) unlock_window();
3538         return result;
3539 }
3540
3541 int BC_WindowBase::get_screen_w(int lock_display, int screen)
3542 {
3543         int result = -1;
3544         if(lock_display) lock_window("BC_WindowBase::get_screen_w");
3545         XineramaScreenInfo *info = top_level->get_xinerama_info(screen);
3546         if( !info ) {
3547                 int width = get_root_w(0);
3548                 int height = get_root_h(0);
3549                 if( (float)width/height > 1.8 ) {
3550                         // If dual head, the screen width is > 16x9
3551                         // but we only want to fill one screen
3552                         // this code assumes the "big" screen is on the right
3553                         int scr_w0 = width / 2;
3554                         switch( height ) {
3555                         case 600:  scr_w0 = 800;   break;
3556                         case 720:  scr_w0 = 1280;  break;
3557                         case 1024: scr_w0 = 1280;  break;
3558                         case 1200: scr_w0 = 1600;  break;
3559                         case 1080: scr_w0 = 1920;  break;
3560                         }
3561                         int scr_w1 = width - scr_w0;
3562                         result = screen > 0 ? scr_w1 :
3563                                 screen == 0 ? scr_w0 :
3564                                 top_level->get_x() < scr_w0 ? scr_w0 : scr_w1;
3565                 }
3566                 else
3567                         result = width;
3568         }
3569         else
3570                 result = info->width;
3571         if(lock_display) unlock_window();
3572         return result;
3573 }
3574
3575 int BC_WindowBase::get_screen_h(int lock_display, int screen)
3576 {
3577         if(lock_display) lock_window("BC_WindowBase::get_screen_h");
3578         XineramaScreenInfo *info = top_level->get_xinerama_info(screen);
3579         int result = info ? info->height : get_root_h(0);
3580         if(lock_display) unlock_window();
3581         return result;
3582 }
3583
3584 // Bottom right corner
3585 int BC_WindowBase::get_x2()
3586 {
3587         return w + x;
3588 }
3589
3590 int BC_WindowBase::get_y2()
3591 {
3592         return y + h;
3593 }
3594
3595 int BC_WindowBase::get_video_on()
3596 {
3597         return video_on;
3598 }
3599
3600 int BC_WindowBase::get_hidden()
3601 {
3602         return top_level->hidden;
3603 }
3604
3605 int BC_WindowBase::cursor_inside()
3606 {
3607         return (top_level->cursor_x >= 0 &&
3608                         top_level->cursor_y >= 0 &&
3609                         top_level->cursor_x < w &&
3610                         top_level->cursor_y < h);
3611 }
3612
3613 BC_WindowBase* BC_WindowBase::get_top_level()
3614 {
3615         return top_level;
3616 }
3617
3618 BC_WindowBase* BC_WindowBase::get_parent()
3619 {
3620         return parent_window;
3621 }
3622
3623 int BC_WindowBase::get_color_model()
3624 {
3625         return top_level->color_model;
3626 }
3627
3628 BC_Resources* BC_WindowBase::get_resources()
3629 {
3630         return &BC_WindowBase::resources;
3631 }
3632
3633 BC_Synchronous* BC_WindowBase::get_synchronous()
3634 {
3635         return BC_WindowBase::resources.get_synchronous();
3636 }
3637
3638 int BC_WindowBase::get_bg_color()
3639 {
3640         return bg_color;
3641 }
3642
3643 void BC_WindowBase::set_bg_color(int color)
3644 {
3645         this->bg_color = color;
3646 }
3647
3648 BC_Pixmap* BC_WindowBase::get_bg_pixmap()
3649 {
3650         return bg_pixmap;
3651 }
3652
3653 void BC_WindowBase::set_active_subwindow(BC_WindowBase *subwindow)
3654 {
3655         top_level->active_subwindow = subwindow;
3656 }
3657
3658 int BC_WindowBase::activate()
3659 {
3660         return 0;
3661 }
3662
3663 int BC_WindowBase::deactivate()
3664 {
3665         if(window_type == MAIN_WINDOW)
3666         {
3667                 if( top_level->active_menubar ) {
3668                         top_level->active_menubar->deactivate();
3669                         top_level->active_menubar = 0;
3670                 }
3671                 if( top_level->active_popup_menu ) {
3672                         top_level->active_popup_menu->deactivate();
3673                         top_level->active_popup_menu = 0;
3674                 }
3675                 if( top_level->active_subwindow ) {
3676                         top_level->active_subwindow->deactivate();
3677                         top_level->active_subwindow = 0;
3678                 }
3679                 if( top_level->motion_events && top_level->last_motion_win == this->win )
3680                         top_level->motion_events = 0;
3681
3682         }
3683         return 0;
3684 }
3685
3686 int BC_WindowBase::cycle_textboxes(int amount)
3687 {
3688         int result = 0;
3689         BC_WindowBase *new_textbox = 0;
3690
3691         if(amount > 0)
3692         {
3693                 BC_WindowBase *first_textbox = 0;
3694                 find_next_textbox(&first_textbox, &new_textbox, result);
3695                 if(!new_textbox) new_textbox = first_textbox;
3696
3697         }
3698         else
3699         if(amount < 0)
3700         {
3701                 BC_WindowBase *last_textbox = 0;
3702                 find_prev_textbox(&last_textbox, &new_textbox, result);
3703                 if(!new_textbox) new_textbox = last_textbox;
3704
3705         }
3706
3707         if(new_textbox != active_subwindow)
3708         {
3709                 deactivate();
3710                 new_textbox->activate();
3711         }
3712
3713         return 0;
3714 }
3715
3716 int BC_WindowBase::find_next_textbox(BC_WindowBase **first_textbox, BC_WindowBase **next_textbox, int &result)
3717 {
3718 // Search subwindows for textbox
3719         for(int i = 0; i < subwindows->total && result < 2; i++)
3720         {
3721                 BC_WindowBase *test_subwindow = subwindows->values[i];