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