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         if(this != top_level)
3197                 top_level->remove_popup(window);
3198         else
3199                 popups.remove(window);
3200 //printf("BC_WindowBase::remove_popup %d size=%d window=%p win=%p\n", __LINE__, popups.size(), window, window->win);
3201 }
3202
3203
3204 BC_WindowBase* BC_WindowBase::add_subwindow(BC_WindowBase *subwindow)
3205 {
3206         subwindows->append(subwindow);
3207
3208         if(subwindow->bg_color == -1) subwindow->bg_color = this->bg_color;
3209
3210 // parent window must be set before the subwindow initialization
3211         subwindow->parent_window = this;
3212         subwindow->top_level = this->top_level;
3213
3214 // Execute derived initialization
3215         subwindow->initialize();
3216         return subwindow;
3217 }
3218
3219
3220 BC_WindowBase* BC_WindowBase::add_tool(BC_WindowBase *subwindow)
3221 {
3222         return add_subwindow(subwindow);
3223 }
3224
3225 int BC_WindowBase::flash(int x, int y, int w, int h, int flush)
3226 {
3227         if( !top_level->flash_enabled ) return 0;
3228 //printf("BC_WindowBase::flash %d %d %d %d %d\n", __LINE__, w, h, this->w, this->h);
3229         set_opaque();
3230         XSetWindowBackgroundPixmap(top_level->display, win, pixmap->opaque_pixmap);
3231         if(x >= 0)
3232         {
3233                 XClearArea(top_level->display, win, x, y, w, h, 0);
3234         }
3235         else
3236         {
3237                 XClearWindow(top_level->display, win);
3238         }
3239
3240         if(flush)
3241                 this->flush();
3242         return 0;
3243 }
3244
3245 int BC_WindowBase::flash(int flush)
3246 {
3247         flash(-1, -1, -1, -1, flush);
3248         return 0;
3249 }
3250
3251 void BC_WindowBase::flush()
3252 {
3253         //if(!get_window_lock())
3254         //      printf("BC_WindowBase::flush %s not locked\n", top_level->title);
3255         // X gets hosed if Flush/Sync are not user locked (at libX11-1.1.5 / libxcb-1.1.91)
3256         //   _XReply deadlocks in condition_wait waiting for xlib lock when waiters!=-1
3257         int locked  = get_window_lock();
3258         if( !locked ) lock_window("BC_WindowBase::flush");
3259         XFlush(top_level->display);
3260         if( !locked ) unlock_window();
3261 }
3262
3263 void BC_WindowBase::sync_display()
3264 {
3265         int locked  = get_window_lock();
3266         if( !locked ) lock_window("BC_WindowBase::sync_display");
3267         XSync(top_level->display, False);
3268         if( !locked ) unlock_window();
3269 }
3270
3271 int BC_WindowBase::get_window_lock()
3272 {
3273 #ifdef SINGLE_THREAD
3274         return BC_Display::display_global->get_display_locked();
3275 #else
3276         return top_level->window_lock;
3277 #endif
3278 }
3279
3280 int BC_WindowBase::lock_window(const char *location)
3281 {
3282         if(top_level && top_level != this)
3283         {
3284                 top_level->lock_window(location);
3285         }
3286         else
3287         if(top_level)
3288         {
3289                 SET_LOCK(this, title, location);
3290 #ifdef SINGLE_THREAD
3291                 BC_Display::lock_display(location);
3292 #else
3293                 XLockDisplay(top_level->display);
3294                 top_level->display_lock_owner = pthread_self();
3295 #endif
3296                 SET_LOCK2
3297                 ++top_level->window_lock;
3298         }
3299         else
3300         {
3301                 printf("BC_WindowBase::lock_window top_level NULL\n");
3302         }
3303         return 0;
3304 }
3305
3306 int BC_WindowBase::unlock_window()
3307 {
3308         if(top_level && top_level != this)
3309         {
3310                 top_level->unlock_window();
3311         }
3312         else
3313         if(top_level)
3314         {
3315                 UNSET_LOCK(this);
3316                 if( !top_level->window_lock ) {
3317                         printf("BC_WindowBase::unlock_window %s not locked\n", title);
3318                         booby();
3319                 }
3320                 if( top_level->window_lock > 0 )
3321                         if( --top_level->window_lock == 0 )
3322                                 top_level->display_lock_owner = 0;
3323 #ifdef SINGLE_THREAD
3324                 BC_Display::unlock_display();
3325 #else
3326                 XUnlockDisplay(top_level->display);
3327 #endif
3328         }
3329         else
3330         {
3331                 printf("BC_WindowBase::unlock_window top_level NULL\n");
3332         }
3333         return 0;
3334 }
3335
3336 int BC_WindowBase::break_lock()
3337 {
3338         if( !top_level ) return 0;
3339         if( top_level != this ) return top_level->break_lock();
3340         if( top_level->display_lock_owner != pthread_self() ) return 0;
3341         if( top_level->window_lock != 1 ) return 0;
3342         UNSET_LOCK(this);
3343         window_lock = 0;
3344         display_lock_owner = 0;
3345 #ifdef SINGLE_THREAD
3346         BC_Display::unlock_display();
3347 #else
3348         XUnlockDisplay(display);
3349 #endif
3350         return 1;
3351 }
3352
3353 void BC_WindowBase::set_done(int return_value)
3354 {
3355         if(done_set) return;
3356         done_set = 1;
3357         if(window_type != MAIN_WINDOW)
3358                 top_level->set_done(return_value);
3359         else
3360         {
3361 #ifdef SINGLE_THREAD
3362                 this->return_value = return_value;
3363                 BC_Display::display_global->arm_completion(this);
3364                 completion_lock->unlock();
3365 #else // SINGLE_THREAD
3366                 init_wait();
3367                 if( !event_thread ) return;
3368                 XEvent *event = new_xevent();
3369                 XClientMessageEvent *ptr = (XClientMessageEvent*)event;
3370                 event->type = ClientMessage;
3371                 ptr->message_type = SetDoneXAtom;
3372                 ptr->format = 32;
3373                 this->return_value = return_value;
3374 // May lock up here because XSendEvent doesn't work too well
3375 // asynchronous with XNextEvent.
3376 // This causes BC_WindowEvents to forward a copy of the event to run_window where
3377 // it is deleted.
3378 // Deletion of event_thread is done at the end of BC_WindowBase::run_window() - by calling the destructor
3379                 put_event(event);
3380 #endif
3381         }
3382 }
3383
3384 void BC_WindowBase::close(int return_value)
3385 {
3386         hide_window();  flush();
3387         set_done(return_value);
3388 }
3389
3390 int BC_WindowBase::grab(BC_WindowBase *window)
3391 {
3392         if( window->active_grab && this != window->active_grab ) return 0;
3393         window->active_grab = this;
3394         this->grab_active = window;
3395         return 1;
3396 }
3397 int BC_WindowBase::ungrab(BC_WindowBase *window)
3398 {
3399         if( window->active_grab && this != window->active_grab ) return 0;
3400         window->active_grab = 0;
3401         this->grab_active = 0;
3402         return 1;
3403 }
3404 int BC_WindowBase::grab_event_count()
3405 {
3406         int result = 0;
3407 #ifndef SINGLE_THREAD
3408         result = grab_active->get_event_count();
3409 #endif
3410         return result;
3411 }
3412 int BC_WindowBase::grab_buttons()
3413 {
3414         XSync(top_level->display, False);
3415         if( XGrabButton(top_level->display, AnyButton, AnyModifier,
3416                         top_level->rootwin, True, ButtonPressMask | ButtonReleaseMask,
3417                         GrabModeAsync, GrabModeSync, None, None) == GrabSuccess ) {
3418                 set_active_subwindow(this);
3419                 return 0;
3420         }
3421         return 1;
3422 }
3423 void BC_WindowBase::ungrab_buttons()
3424 {
3425         XUngrabButton(top_level->display, AnyButton, AnyModifier, top_level->rootwin);
3426         set_active_subwindow(0);
3427         unhide_cursor();
3428 }
3429 void BC_WindowBase::grab_cursor()
3430 {
3431         Cursor cursor_grab = get_cursor_struct(GRABBED_CURSOR);
3432         XGrabPointer(top_level->display, top_level->rootwin, True,
3433                 PointerMotionMask | ButtonPressMask | ButtonReleaseMask,
3434                 GrabModeAsync, GrabModeAsync, None, cursor_grab, CurrentTime);
3435 }
3436 void BC_WindowBase::ungrab_cursor()
3437 {
3438         XUngrabPointer(top_level->display, CurrentTime);
3439 }
3440
3441 // for get_root_w/h
3442 //   WidthOfScreen/HeightOfScreen of XDefaultScreenOfDisplay
3443 //   this is the bounding box of all the screens
3444
3445 int BC_WindowBase::get_root_w(int lock_display)
3446 {
3447         if(lock_display) lock_window("BC_WindowBase::get_root_w");
3448         Screen *def_screen = XDefaultScreenOfDisplay(top_level->display);
3449         int result = WidthOfScreen(def_screen);
3450         if(lock_display) unlock_window();
3451         return result;
3452 }
3453
3454 int BC_WindowBase::get_root_h(int lock_display)
3455 {
3456         if(lock_display) lock_window("BC_WindowBase::get_root_h");
3457         Screen *def_screen = XDefaultScreenOfDisplay(top_level->display);
3458         int result = HeightOfScreen(def_screen);
3459         if(lock_display) unlock_window();
3460         return result;
3461 }
3462
3463 XineramaScreenInfo *
3464 BC_WindowBase::get_xinerama_info(int screen)
3465 {
3466         if( !xinerama_info || !xinerama_screens ) return 0;
3467         if( screen >= 0 ) {
3468                 for( int i=0; i<xinerama_screens; ++i )
3469                         if( xinerama_info[i].screen_number == screen )
3470                                 return &xinerama_info[i];
3471                 return 0;
3472         }
3473         int top_x = get_x(), top_y = get_y();
3474         if(  BC_DisplayInfo::left_border >= 0 ) top_x +=  BC_DisplayInfo::left_border;
3475         if(  BC_DisplayInfo::top_border >= 0 ) top_y +=  BC_DisplayInfo::top_border;
3476         for( int i=0; i<xinerama_screens; ++i ) {
3477                 int scr_y = top_y - xinerama_info[i].y_org;
3478                 if( scr_y < 0 || scr_y >= xinerama_info[i].height ) continue;
3479                 int scr_x = top_x - xinerama_info[i].x_org;
3480                 if( scr_x >= 0 && scr_x < xinerama_info[i].width )
3481                         return &xinerama_info[i];
3482         }
3483         return 0;
3484 }
3485
3486 void BC_WindowBase::get_fullscreen_geometry(int &wx, int &wy, int &ww, int &wh)
3487 {
3488         XineramaScreenInfo *info = top_level->get_xinerama_info(-1);
3489         if( info ) {
3490                 wx = info->x_org;  wy = info->y_org;
3491                 ww = info->width;  wh = info->height;
3492         }
3493         else {
3494                 wx = get_screen_x(0, -1);
3495                 wy = get_screen_y(0, -1);
3496                 int scr_w0 = get_screen_w(0, 0);
3497                 int root_w = get_root_w(0);
3498                 int root_h = get_root_h(0);
3499                 if( root_w > scr_w0 ) { // multi-headed
3500                         if( wx >= scr_w0 ) {
3501                                 // assumes right side is the big one
3502                                 ww = root_w - scr_w0;
3503                                 wh = root_h;
3504                         }
3505                         else {
3506                                 // use same aspect ratio to compute left height
3507                                 ww = scr_w0;
3508                                 wh = (w*root_h) / (root_w-scr_w0);
3509                         }
3510                 }
3511                 else {
3512                         ww = root_w;
3513                         wh = root_h;
3514                 }
3515         }
3516 }
3517
3518 int BC_WindowBase::get_screen_x(int lock_display, int screen)
3519 {
3520         int result = -1;
3521         if(lock_display) lock_window("BC_WindowBase::get_screen_x");
3522         XineramaScreenInfo *info = top_level->get_xinerama_info(screen);
3523         if( !info ) {
3524                 result = 0;
3525                 int root_w = get_root_w(0);
3526                 int root_h = get_root_h(0);
3527 // Shift X based on position of current window if dual head
3528                 if( (float)root_w/root_h > 1.8 ) {
3529                         root_w = get_screen_w(0, 0);
3530                         if( top_level->get_x() >= root_w )
3531                                 result = root_w;
3532                 }
3533         }
3534         else
3535                 result = info->x_org;
3536         if(lock_display) unlock_window();
3537         return result;
3538 }
3539
3540 int BC_WindowBase::get_screen_y(int lock_display, int screen)
3541 {
3542         if(lock_display) lock_window("BC_WindowBase::get_screen_y");
3543         XineramaScreenInfo *info = top_level->get_xinerama_info(screen);
3544         int result = !info ? 0 : info->y_org;
3545         if(lock_display) unlock_window();
3546         return result;
3547 }
3548
3549 int BC_WindowBase::get_screen_w(int lock_display, int screen)
3550 {
3551         int result = -1;
3552         if(lock_display) lock_window("BC_WindowBase::get_screen_w");
3553         XineramaScreenInfo *info = top_level->get_xinerama_info(screen);
3554         if( !info ) {
3555                 int width = get_root_w(0);
3556                 int height = get_root_h(0);
3557                 if( (float)width/height > 1.8 ) {
3558                         // If dual head, the screen width is > 16x9
3559                         // but we only want to fill one screen
3560                         // this code assumes the "big" screen is on the right
3561                         int scr_w0 = width / 2;
3562                         switch( height ) {
3563                         case 600:  scr_w0 = 800;   break;
3564                         case 720:  scr_w0 = 1280;  break;
3565                         case 1024: scr_w0 = 1280;  break;
3566                         case 1200: scr_w0 = 1600;  break;
3567                         case 1080: scr_w0 = 1920;  break;
3568                         }
3569                         int scr_w1 = width - scr_w0;
3570                         result = screen > 0 ? scr_w1 :
3571                                 screen == 0 ? scr_w0 :
3572                                 top_level->get_x() < scr_w0 ? scr_w0 : scr_w1;
3573                 }
3574                 else
3575                         result = width;
3576         }
3577         else
3578                 result = info->width;
3579         if(lock_display) unlock_window();
3580         return result;
3581 }
3582
3583 int BC_WindowBase::get_screen_h(int lock_display, int screen)
3584 {
3585         if(lock_display) lock_window("BC_WindowBase::get_screen_h");
3586         XineramaScreenInfo *info = top_level->get_xinerama_info(screen);
3587         int result = info ? info->height : get_root_h(0);
3588         if(lock_display) unlock_window();
3589         return result;
3590 }
3591
3592 // Bottom right corner
3593 int BC_WindowBase::get_x2()
3594 {
3595         return w + x;
3596 }
3597
3598 int BC_WindowBase::get_y2()
3599 {
3600         return y + h;
3601 }
3602
3603 int BC_WindowBase::get_video_on()
3604 {
3605         return video_on;
3606 }
3607
3608 int BC_WindowBase::get_hidden()
3609 {
3610         return top_level->hidden;
3611 }
3612
3613 int BC_WindowBase::cursor_inside()
3614 {
3615         return (top_level->cursor_x >= 0 &&
3616                         top_level->cursor_y >= 0 &&
3617                         top_level->cursor_x < w &&
3618                         top_level->cursor_y < h);
3619 }
3620
3621 BC_WindowBase* BC_WindowBase::get_top_level()
3622 {
3623         return top_level;
3624 }
3625
3626 BC_WindowBase* BC_WindowBase::get_parent()
3627 {
3628         return parent_window;
3629 }
3630
3631 int BC_WindowBase::get_color_model()
3632 {
3633         return top_level->color_model;
3634 }
3635
3636 BC_Resources* BC_WindowBase::get_resources()
3637 {
3638         return &BC_WindowBase::resources;
3639 }
3640
3641 BC_Synchronous* BC_WindowBase::get_synchronous()
3642 {
3643         return BC_WindowBase::resources.get_synchronous();
3644 }
3645
3646 int BC_WindowBase::get_bg_color()
3647 {
3648         return bg_color;
3649 }
3650
3651 void BC_WindowBase::set_bg_color(int color)
3652 {
3653         this->bg_color = color;
3654 }
3655
3656 BC_Pixmap* BC_WindowBase::get_bg_pixmap()
3657 {
3658         return bg_pixmap;
3659 }
3660
3661 void BC_WindowBase::set_active_subwindow(BC_WindowBase *subwindow)
3662 {
3663         top_level->active_subwindow = subwindow;
3664 }
3665
3666 int BC_WindowBase::activate()
3667 {
3668         return 0;
3669 }
3670
3671 int BC_WindowBase::deactivate()
3672 {
3673         if(window_type == MAIN_WINDOW)
3674         {
3675                 if( top_level->active_menubar ) {
3676                         top_level->active_menubar->deactivate();
3677                         top_level->active_menubar = 0;
3678                 }
3679                 if( top_level->active_popup_menu ) {
3680                         top_level->active_popup_menu->deactivate();
3681                         top_level->active_popup_menu = 0;
3682                 }
3683                 if( top_level->active_subwindow ) {
3684                         top_level->active_subwindow->deactivate();
3685                         top_level->active_subwindow = 0;
3686                 }
3687                 if( top_level->motion_events && top_level->last_motion_win == this->win )
3688                         top_level->motion_events = 0;
3689
3690         }
3691         return 0;
3692 }
3693
3694 int BC_WindowBase::cycle_textboxes(int amount)
3695 {
3696         int result = 0;
3697         BC_WindowBase *new_textbox = 0;
3698
3699         if(amount > 0)
3700         {
3701                 BC_WindowBase *first_textbox = 0;
3702                 find_next_textbox(&first_textbox, &new_textbox, result);
3703                 if(!new_textbox) new_textbox = first_textbox;
3704
3705         }
3706         else
3707         if(amount < 0)
3708         {
3709                 BC_WindowBase *last_textbox = 0;
3710                 find_prev_textbox(&last_textbox, &new_textbox, result);
3711                 if(!new_textbox) new_textbox = last_textbox;
3712
3713         }
3714
3715         if(new_textbox != active_subwindow)
3716         {
3717                 deactivate();
3718                 new_textbox->activate();
3719         }
3720
3721         return 0;
3722 }
3723
3724 int BC_WindowBase::find_next_textbox(BC_WindowBase **first_textbox, BC_WindowBase **next_textbox, int &result)
3725 {
3726 // Search subwindows for textbox
3727         for(int i = 0; i < subwindows->total && result < 2; i++)
3728         {
3729                 BC_WindowBase *test_subwindow = subwindows->values[i];
3730                 test_subwindow->find_next_textbox(first_textbox, next_textbox, result);
3731         }
3732
3733         if(result < 2)
3734         {
3735                 if(uses_text())
3736                 {
3737                         if(!*first_textbox) *first_textbox = this;
3738
3739                         if(result < 1)
3740                         {
3741                                 if(top_level->active_subwindow == this)
3742                                         result++;
3743                         }
3744                         else
3745                         {
3746                                 result++;
3747                                 *next_textbox = this;
3748                         }
3749                 }
3750         }
3751         return 0;
3752 }
3753
3754 int BC_WindowBase::find_prev_textbox(BC_WindowBase **last_textbox, BC_WindowBase **prev_textbox, int &result)
3755 {
3756         if(result < 2)
3757         {
3758                 if(uses_text())
3759                 {
3760                         if(!*last_textbox) *last_textbox = this;
3761
3762                         if(result < 1)
3763                         {
3764                                 if(top_level->active_subwindow == this)
3765                                         result++;
3766                         }
3767                         else
3768                         {
3769                                 result++;
3770                                 *prev_textbox = this;
3771                         }
3772                 }
3773         }
3774
3775 // Search subwindows for textbox
3776         for(int i = subwindows->total - 1; i >= 0 && result < 2; i--)
3777         {
3778                 BC_WindowBase *test_subwindow = subwindows->values[i];
3779                 test_subwindow->find_prev_textbox(last_textbox, prev_textbox, result);
3780         }
3781         return 0;
3782 }
3783
3784 BC_Clipboard* BC_WindowBase::get_clipboard()
3785 {
3786 #ifdef SINGLE_THREAD
3787         return BC_Display::display_global->clipboard;
3788 #else
3789         return top_level->clipboard;
3790 #endif
3791 }
3792
3793 Atom BC_WindowBase::to_clipboard(const char *data, long len, int clipboard_num)
3794 {
3795         return get_clipboard()->to_clipboard(this, data, len, clipboard_num);
3796 }
3797
3798 long BC_WindowBase::from_clipboard(char *data, long maxlen, int clipboard_num)
3799 {
3800         return get_clipboard()->from_clipboard(data, maxlen, clipboard_num);
3801 }
3802
3803 long BC_WindowBase::clipboard_len(int clipboard_num)
3804 {
3805         return get_clipboard()->clipboard_len(clipboard_num);
3806 }
3807
3808 int BC_WindowBase::do_selection_clear(Window win)
3809 {
3810         top_level->event_win = win;
3811         return dispatch_selection_clear();
3812 }
3813
3814 int BC_WindowBase::dispatch_selection_clear()
3815 {
3816         int result = 0;
3817         for( int i=0; i<subwindows->total && !result; ++i )
3818                 result = subwindows->values[i]->dispatch_selection_clear();
3819         if( !result )
3820                 result = selection_clear_event();
3821         return result;
3822 }
3823
3824
3825 void BC_WindowBase::get_relative_cursor(int &x, int &y, int lock_window)
3826 {
3827         int abs_x, abs_y, win_x, win_y;
3828         unsigned int temp_mask;
3829         Window temp_win;
3830
3831         if(lock_window) this->lock_window("BC_WindowBase::get_relative_cursor");
3832         XQueryPointer(top_level->display, top_level->win,
3833            &temp_win, &temp_win, &abs_x, &abs_y, &win_x, &win_y,
3834            &temp_mask);
3835
3836         XTranslateCoordinates(top_level->display, top_level->rootwin,
3837            win, abs_x, abs_y, &x, &y, &temp_win);
3838         if(lock_window) this->unlock_window();
3839 }
3840 int BC_WindowBase::get_relative_cursor_x(int lock_window)
3841 {
3842         int x, y;
3843         get_relative_cursor(x, y, lock_window);
3844         return x;
3845 }
3846 int BC_WindowBase::get_relative_cursor_y(int lock_window)
3847 {
3848         int x, y;
3849         get_relative_cursor(x, y, lock_window);
3850         return y;
3851 }
3852
3853 void BC_WindowBase::get_abs_cursor(int &abs_x, int &abs_y, int lock_window)
3854 {
3855         int win_x, win_y;
3856         unsigned int temp_mask;
3857         Window temp_win;
3858
3859         if(lock_window) this->lock_window("BC_WindowBase::get_abs_cursor");
3860         XQueryPointer(top_level->display, top_level->win,
3861                 &temp_win, &temp_win, &abs_x, &abs_y, &win_x, &win_y,
3862                 &temp_mask);
3863         if(lock_window) this->unlock_window();
3864 }
3865 int BC_WindowBase::get_abs_cursor_x(int lock_window)
3866 {
3867         int abs_x, abs_y;
3868         get_abs_cursor(abs_x, abs_y, lock_window);
3869         return abs_x;
3870 }
3871 int BC_WindowBase::get_abs_cursor_y(int lock_window)
3872 {
3873         int abs_x, abs_y;
3874         get_abs_cursor(abs_x, abs_y, lock_window);
3875         return abs_y;
3876 }
3877
3878 void BC_WindowBase::get_pop_cursor(int &px, int &py, int lock_window)
3879 {
3880         int margin = 100;
3881         get_abs_cursor(px, py, lock_window);
3882         if( px < margin ) px = margin;
3883         if( py < margin ) py = margin;
3884         int wd = get_screen_w(lock_window,-1) - margin;
3885         if( px > wd ) px = wd;
3886         int ht = get_screen_h(lock_window,-1) - margin;
3887         if( py > ht ) py = ht;
3888 }
3889 int BC_WindowBase::get_pop_cursor_x(int lock_window)
3890 {
3891         int px, py;
3892         get_pop_cursor(px, py, lock_window);
3893         return px;
3894 }
3895 int BC_WindowBase::get_pop_cursor_y(int lock_window)
3896 {
3897         int px, py;
3898         get_pop_cursor(px, py, lock_window);
3899         return py;
3900 }
3901
3902 int BC_WindowBase::match_window(Window win)
3903 {
3904         if (this->win == win) return 1;
3905         int result = 0;
3906         for(int i = 0; i < subwindows->total; i++) {
3907                 result = subwindows->values[i]->match_window(win);
3908                 if (result) return result;
3909         }
3910         return 0;
3911
3912 }
3913
3914 int BC_WindowBase::get_cursor_over_window()
3915 {
3916         int abs_x, abs_y, win_x, win_y;
3917         unsigned int mask_return;
3918         Window root_return, child_return;
3919
3920         int ret = XQueryPointer(top_level->display, top_level->rootwin,
3921                 &root_return, &child_return, &abs_x, &abs_y,
3922                 &win_x, &win_y, &mask_return);
3923         if( ret && child_return == None ) ret = 0;
3924         if( ret && win != child_return )
3925                 ret = top_level->match_window(child_return);
3926 // query pointer can return a window manager window with this top_level as a child
3927 //  for kde this can be two levels deep
3928         unsigned int nchildren_return = 0;
3929         Window parent_return, *children_return = 0;
3930         Window top_win = top_level->win;
3931         while( !ret && top_win != top_level->rootwin && top_win != root_return &&
3932                 XQueryTree(top_level->display, top_win, &root_return,
3933                         &parent_return, &children_return, &nchildren_return) ) {
3934                 if( children_return ) XFree(children_return);
3935                 if( (top_win=parent_return) == child_return ) ret = 1;
3936         }
3937         return ret;
3938 }
3939
3940 int BC_WindowBase::cursor_above()
3941 {
3942         int rx, ry;
3943         get_relative_cursor(rx, ry);
3944         return rx < 0 || rx >= get_w() ||
3945                 ry < 0 || ry >= get_h() ? 0 : 1;
3946 }
3947
3948 int BC_WindowBase::get_drag_x()
3949 {
3950         return top_level->drag_x;
3951 }
3952
3953 int BC_WindowBase::get_drag_y()
3954 {
3955         return top_level->drag_y;
3956 }
3957
3958 int BC_WindowBase::get_cursor_x()
3959 {
3960         return top_level->cursor_x;
3961 }
3962
3963 int BC_WindowBase::get_cursor_y()
3964 {
3965         return top_level->cursor_y;
3966 }
3967
3968 int BC_WindowBase::dump_windows()
3969 {
3970         printf("\tBC_WindowBase::dump_windows window=%p win=%p '%s', %dx%d+%d+%d %s\n",
3971                 this, (void*)this->win, title, w,h,x,y, typeid(*this).name());
3972         for(int i = 0; i < subwindows->size(); i++)
3973                 subwindows->get(i)->dump_windows();
3974         for(int i = 0; i < popups.size(); i++) {
3975                 BC_WindowBase *p = popups[i];
3976                 printf("\tBC_WindowBase::dump_windows popup=%p win=%p '%s', %dx%d+%d+%d %s\n",
3977                         p, (void*)p->win, p->title, p->w,p->h,p->x,p->y, typeid(*p).name());
3978         }
3979         return 0;
3980 }
3981
3982 int BC_WindowBase::is_event_win()
3983 {
3984         return this->win == top_level->event_win;
3985 }
3986
3987 void BC_WindowBase::set_dragging(int value)
3988 {
3989         is_dragging = value;
3990 }
3991
3992 int BC_WindowBase::get_dragging()
3993 {
3994         return is_dragging;
3995 }
3996
3997 int BC_WindowBase::get_buttonpress()
3998 {
3999         return top_level->button_number;
4000 }
4001
4002 int BC_WindowBase::get_button_down()
4003 {
4004         return top_level->button_down;
4005 }
4006
4007 int BC_WindowBase::alt_down()
4008 {
4009         return top_level->alt_mask;
4010 }
4011
4012 int BC_WindowBase::shift_down()
4013 {
4014         return top_level->shift_mask;
4015 }
4016
4017 int BC_WindowBase::ctrl_down()
4018 {
4019         return top_level->ctrl_mask;
4020 }
4021
4022 wchar_t* BC_WindowBase::get_wkeystring(int *length)
4023 {
4024         if(length)
4025                 *length = top_level->wkey_string_length;
4026         return top_level->wkey_string;
4027 }
4028
4029 #ifdef X_HAVE_UTF8_STRING
4030 char* BC_WindowBase::get_keypress_utf8()
4031 {
4032         return top_level->key_pressed_utf8;
4033 }
4034 #endif
4035
4036
4037 int BC_WindowBase::get_keypress()
4038 {
4039         return top_level->key_pressed;
4040 }
4041
4042 int BC_WindowBase::get_double_click()
4043 {
4044         return top_level->double_click;
4045 }
4046
4047 int BC_WindowBase::get_triple_click()
4048 {
4049         return top_level->triple_click;
4050 }
4051
4052 int BC_WindowBase::get_bgcolor()
4053 {
4054         return bg_color;
4055 }
4056
4057 int BC_WindowBase::resize_window(int w, int h)
4058 {
4059         if(this->w == w && this->h == h) return 0;
4060
4061         if(window_type == MAIN_WINDOW && !allow_resize)
4062         {
4063                 XSizeHints size_hints;
4064                 size_hints.flags = PSize | PMinSize | PMaxSize;
4065                 size_hints.width = w;
4066                 size_hints.height = h;
4067                 size_hints.min_width = w;
4068                 size_hints.max_width = w;
4069                 size_hints.min_height = h;
4070                 size_hints.max_height = h;
4071                 XSetNormalHints(top_level->display, win, &size_hints);
4072         }
4073         XResizeWindow(top_level->display, win, w, h);
4074
4075         this->w = w;
4076         this->h = h;
4077         delete pixmap;
4078         pixmap = new BC_Pixmap(this, w, h);
4079
4080 // Propagate to menubar
4081         for(int i = 0; i < subwindows->total; i++)
4082         {
4083                 subwindows->values[i]->dispatch_resize_event(w, h);
4084         }
4085
4086         draw_background(0, 0, w, h);
4087         if(top_level == this && get_resources()->recursive_resizing)
4088                 resize_history.append(new BC_ResizeCall(w, h));
4089         return 0;
4090 }
4091
4092 // The only way for resize events to be propagated is by updating the internal w and h
4093 int BC_WindowBase::resize_event(int w, int h)
4094 {
4095         if(window_type == MAIN_WINDOW)
4096         {
4097                 this->w = w;
4098                 this->h = h;
4099         }
4100         return 0;
4101 }
4102
4103 int BC_WindowBase::reposition_window(int x, int y)
4104 {
4105         reposition_window(x, y, -1, -1);
4106         return 0;
4107 }
4108
4109
4110 int BC_WindowBase::reposition_window(int x, int y, int w, int h)
4111 {
4112         int resize = 0;
4113
4114 // Some tools set their own dimensions before calling this, causing the
4115 // resize check to skip.
4116         this->x = x;
4117         this->y = y;
4118
4119         if(w > 0 && w != this->w)
4120         {
4121                 resize = 1;
4122                 this->w = w;
4123         }
4124
4125         if(h > 0 && h != this->h)
4126         {
4127                 resize = 1;
4128                 this->h = h;
4129         }
4130
4131 //printf("BC_WindowBase::reposition_window %d %d %d\n", translation_count, x_correction, y_correction);
4132
4133         if(this->w <= 0)
4134                 printf("BC_WindowBase::reposition_window this->w == %d\n", this->w);
4135         if(this->h <= 0)
4136                 printf("BC_WindowBase::reposition_window this->h == %d\n", this->h);
4137
4138         if(translation_count && window_type == MAIN_WINDOW)
4139         {
4140 // KDE shifts window right and down.
4141 // FVWM leaves window alone and adds border around it.
4142                 XMoveResizeWindow(top_level->display,
4143                         win,
4144                         x - BC_DisplayInfo::auto_reposition_x,
4145                         y - BC_DisplayInfo::auto_reposition_y,
4146                         this->w,
4147                         this->h);
4148         }
4149         else
4150         {
4151                 XMoveResizeWindow(top_level->display,
4152                         win,
4153                         x,
4154                         y,
4155                         this->w,
4156                         this->h);
4157         }
4158
4159         if(resize)
4160         {
4161                 delete pixmap;
4162                 pixmap = new BC_Pixmap(this, this->w, this->h);
4163                 clear_box(0,0, this->w, this->h);
4164 // Propagate to menubar
4165                 for(int i = 0; i < subwindows->total; i++)
4166                 {
4167                         subwindows->values[i]->dispatch_resize_event(this->w, this->h);
4168                 }
4169
4170 //              draw_background(0, 0, w, h);
4171         }
4172
4173         return 0;
4174 }
4175
4176 int BC_WindowBase::reposition_window_relative(int dx, int dy, int w, int h)
4177 {
4178         return reposition_window(get_x()+dx, get_y()+dy, w, h);
4179 }
4180
4181 int BC_WindowBase::reposition_window_relative(int dx, int dy)
4182 {
4183         return reposition_window_relative(dx, dy, -1, -1);
4184 }
4185
4186 void BC_WindowBase::set_tooltips(int v)
4187 {
4188         get_resources()->tooltips_enabled = v;
4189 }
4190
4191 void BC_WindowBase::set_force_tooltip(int v)
4192 {
4193         force_tooltip = v;
4194 }
4195
4196 int BC_WindowBase::raise_window(int do_flush)
4197 {
4198         XRaiseWindow(top_level->display, win);
4199         if(do_flush) XFlush(top_level->display);
4200         return 0;
4201 }
4202
4203 int BC_WindowBase::lower_window(int do_flush)
4204 {
4205         XLowerWindow(top_level->display, win);
4206         if(do_flush) XFlush(top_level->display);
4207         return 0;
4208 }
4209
4210 void BC_WindowBase::set_background(VFrame *bitmap)
4211 {
4212         if(bg_pixmap && !shared_bg_pixmap) delete bg_pixmap;
4213
4214         bg_pixmap = new BC_Pixmap(this, bitmap, PIXMAP_OPAQUE);
4215         shared_bg_pixmap = 0;
4216         draw_background(0, 0, w, h);
4217 }
4218
4219 void BC_WindowBase::put_title(const char *text)
4220 {
4221         char *cp = this->title, *ep = cp+sizeof(this->title)-1;
4222         for( const unsigned char *bp = (const unsigned char *)text; *bp && cp<ep; ++bp )
4223                 *cp++ = *bp >= ' ' ? *bp : ' ';
4224         *cp = 0;
4225 }
4226
4227 void BC_WindowBase::set_title(const char *text, int utf8)
4228 {
4229 // utf8>0: wm + net_wm, utf8=0: wm only,  utf<0: net_wm only
4230         put_title(text);
4231         const unsigned char *wm_title = (const unsigned char *)title;
4232         int title_len = strlen((const char *)title);
4233         if( utf8 >= 0 ) {
4234                 Atom xa_wm_name = XA_WM_NAME;
4235                 Atom xa_icon_name = XA_WM_ICON_NAME;
4236                 Atom xa_string = XA_STRING;
4237                 XChangeProperty(display, win, xa_wm_name, xa_string, 8,
4238                                 PropModeReplace, wm_title, title_len);
4239                 XChangeProperty(display, win, xa_icon_name, xa_string, 8,
4240                                 PropModeReplace, wm_title, title_len);
4241         }
4242         if( utf8 != 0 ) {
4243                 Atom xa_net_wm_name = XInternAtom(display, "_NET_WM_NAME", True);
4244                 Atom xa_net_icon_name = XInternAtom(display, "_NET_WM_ICON_NAME", True);
4245                 Atom xa_utf8_string = XInternAtom(display, "UTF8_STRING", True);
4246                 XChangeProperty(display, win, xa_net_wm_name, xa_utf8_string, 8,
4247                                         PropModeReplace, wm_title, title_len);
4248                 XChangeProperty(display, win, xa_net_icon_name, xa_utf8_string, 8,
4249                                         PropModeReplace, wm_title, title_len);
4250         }
4251         flush();
4252 }
4253
4254 const char *BC_WindowBase::get_title()
4255 {
4256         return title;
4257 }
4258
4259 int BC_WindowBase::get_toggle_value()
4260 {
4261         return toggle_value;
4262 }
4263
4264 int BC_WindowBase::get_toggle_drag()
4265 {
4266         return toggle_drag;
4267 }
4268
4269 int BC_WindowBase::set_icon(VFrame *data)
4270 {
4271         if(icon_pixmap) delete icon_pixmap;
4272         icon_pixmap = new BC_Pixmap(top_level, data, PIXMAP_ALPHA, 1);
4273
4274         if(icon_window) delete icon_window;
4275         icon_window = new BC_Popup(this,
4276                 (int)BC_INFINITY,
4277                 (int)BC_INFINITY,
4278                 icon_pixmap->get_w(),
4279                 icon_pixmap->get_h(),
4280                 -1,
4281                 1, // All windows are hidden initially
4282                 icon_pixmap);
4283
4284         XWMHints wm_hints;
4285         wm_hints.flags = WindowGroupHint | IconPixmapHint | IconMaskHint | IconWindowHint;
4286         wm_hints.icon_pixmap = icon_pixmap->get_pixmap();
4287         wm_hints.icon_mask = icon_pixmap->get_alpha();
4288         wm_hints.icon_window = icon_window->win;
4289         wm_hints.window_group = XGroupLeader;
4290
4291 // for(int i = 0; i < 1000; i++)
4292 // printf("02x ", icon_pixmap->get_alpha()->get_row_pointers()[0][i]);
4293 // printf("\n");
4294
4295         XSetWMHints(top_level->display, top_level->win, &wm_hints);
4296         XSync(top_level->display, 0);
4297         return 0;
4298 }
4299
4300 int BC_WindowBase::set_w(int w)
4301 {
4302         this->w = w;
4303         return 0;
4304 }
4305
4306 int BC_WindowBase::set_h(int h)
4307 {
4308         this->h = h;
4309         return 0;
4310 }
4311
4312 int BC_WindowBase::load_defaults(BC_Hash *defaults)
4313 {
4314         BC_Resources *resources = get_resources();
4315         char string[BCTEXTLEN];
4316         int newest_id = - 1;
4317         for(int i = 0; i < FILEBOX_HISTORY_SIZE; i++)
4318         {
4319                 sprintf(string, "FILEBOX_HISTORY_PATH%d", i);
4320                 resources->filebox_history[i].path[0] = 0;
4321                 defaults->get(string, resources->filebox_history[i].path);
4322                 sprintf(string, "FILEBOX_HISTORY_ID%d", i);
4323                 resources->filebox_history[i].id = defaults->get(string, resources->get_id());
4324                 if(resources->filebox_history[i].id > newest_id)
4325                         newest_id = resources->filebox_history[i].id;
4326         }
4327
4328         resources->filebox_id = newest_id + 1;
4329         resources->filebox_mode = defaults->get("FILEBOX_MODE", get_resources()->filebox_mode);
4330         resources->filebox_w = defaults->get("FILEBOX_W", get_resources()->filebox_w);
4331         resources->filebox_h = defaults->get("FILEBOX_H", get_resources()->filebox_h);
4332         resources->filebox_columntype[0] = defaults->get("FILEBOX_TYPE0", resources->filebox_columntype[0]);
4333         resources->filebox_columntype[1] = defaults->get("FILEBOX_TYPE1", resources->filebox_columntype[1]);
4334         resources->filebox_columntype[2] = defaults->get("FILEBOX_TYPE2", resources->filebox_columntype[2]);
4335         resources->filebox_columntype[3] = defaults->get("FILEBOX_TYPE3", resources->filebox_columntype[3]);
4336         resources->filebox_columnwidth[0] = defaults->get("FILEBOX_WIDTH0", resources->filebox_columnwidth[0]);
4337         resources->filebox_columnwidth[1] = defaults->get("FILEBOX_WIDTH1", resources->filebox_columnwidth[1]);
4338         resources->filebox_columnwidth[2] = defaults->get("FILEBOX_WIDTH2", resources->filebox_columnwidth[2]);
4339         resources->filebox_columnwidth[3] = defaults->get("FILEBOX_WIDTH3", resources->filebox_columnwidth[3]);
4340         resources->filebox_size_format = defaults->get("FILEBOX_SIZE_FORMAT", get_resources()->filebox_size_format);
4341         defaults->get("FILEBOX_FILTER", resources->filebox_filter);
4342         return 0;
4343 }
4344
4345 int BC_WindowBase::save_defaults(BC_Hash *defaults)
4346 {
4347         BC_Resources *resources = get_resources();
4348         char string[BCTEXTLEN];
4349         for(int i = 0; i < FILEBOX_HISTORY_SIZE; i++)
4350         {
4351                 sprintf(string, "FILEBOX_HISTORY_PATH%d", i);
4352                 defaults->update(string, resources->filebox_history[i].path);
4353                 sprintf(string, "FILEBOX_HISTORY_ID%d", i);
4354                 defaults->update(string, resources->filebox_history[i].id);
4355         }
4356         defaults->update("FILEBOX_MODE", resources->filebox_mode);
4357         defaults->update("FILEBOX_W", resources->filebox_w);
4358         defaults->update("FILEBOX_H", resources->filebox_h);
4359         defaults->update("FILEBOX_TYPE0", resources->filebox_columntype[0]);
4360         defaults->update("FILEBOX_TYPE1", resources->filebox_columntype[1]);
4361         defaults->update("FILEBOX_TYPE2", resources->filebox_columntype[2]);
4362         defaults->update("FILEBOX_TYPE3", resources->filebox_columntype[3]);
4363         defaults->update("FILEBOX_WIDTH0", resources->filebox_columnwidth[0]);
4364         defaults->update("FILEBOX_WIDTH1", resources->filebox_columnwidth[1]);
4365         defaults->update("FILEBOX_WIDTH2", resources->filebox_columnwidth[2]);
4366         defaults->update("FILEBOX_WIDTH3", resources->filebox_columnwidth[3]);
4367         defaults->update("FILEBOX_FILTER", resources->filebox_filter);
4368         defaults->update("FILEBOX_SIZE_FORMAT", get_resources()->filebox_size_format);
4369         return 0;
4370 }
4371
4372
4373
4374 // For some reason XTranslateCoordinates can take a long time to return.
4375 // We work around this by only calling it when the event windows are different.
4376 void BC_WindowBase::translate_coordinates(Window src_w,
4377                 Window dest_w,
4378                 int src_x,
4379                 int src_y,
4380                 int *dest_x_return,
4381                 int *dest_y_return)
4382 {
4383         Window tempwin = 0;
4384 //Timer timer;
4385 //timer.update();
4386         if(src_w == dest_w)
4387         {
4388                 *dest_x_return = src_x;
4389                 *dest_y_return = src_y;
4390         }
4391         else
4392         {
4393                 XTranslateCoordinates(top_level->display,
4394                         src_w,
4395                         dest_w,
4396                         src_x,
4397                         src_y,
4398                         dest_x_return,
4399                         dest_y_return,
4400                         &tempwin);
4401 //printf("BC_WindowBase::translate_coordinates 1 %lld\n", timer.get_difference());
4402         }
4403 }
4404
4405 void BC_WindowBase::get_root_coordinates(int x, int y, int *abs_x, int *abs_y)
4406 {
4407         translate_coordinates(win, top_level->rootwin, x, y, abs_x, abs_y);
4408 }
4409
4410 void BC_WindowBase::get_win_coordinates(int abs_x, int abs_y, int *x, int *y)
4411 {
4412         translate_coordinates(top_level->rootwin, win, abs_x, abs_y, x, y);
4413 }
4414
4415
4416
4417
4418
4419
4420 #ifdef HAVE_LIBXXF86VM
4421 void BC_WindowBase::closest_vm(int *vm, int *width, int *height)
4422 {
4423         int foo,bar;
4424         *vm = 0;
4425         if(XF86VidModeQueryExtension(top_level->display,&foo,&bar)) {
4426                 int vm_count,i;
4427                 XF86VidModeModeInfo **vm_modelines;
4428                 XF86VidModeGetAllModeLines(top_level->display,
4429                         XDefaultScreen(top_level->display), &vm_count,&vm_modelines);
4430                 for( i = 0; i < vm_count; i++ ) {
4431                         if( vm_modelines[i]->hdisplay < vm_modelines[*vm]->hdisplay &&
4432                             vm_modelines[i]->hdisplay >= *width )
4433                                 *vm = i;
4434                 }
4435                 display = top_level->display;
4436                 if( vm_modelines[*vm]->hdisplay == *width )
4437                         *vm = -1;
4438                 else {
4439                         *width = vm_modelines[*vm]->hdisplay;
4440                         *height = vm_modelines[*vm]->vdisplay;
4441                 }
4442         }
4443 }
4444
4445 void BC_WindowBase::scale_vm(int vm)
4446 {
4447         int foo,bar,dotclock;
4448         if( XF86VidModeQueryExtension(top_level->display,&foo,&bar) ) {
4449                 int vm_count;
4450                 XF86VidModeModeInfo **vm_modelines;
4451                 XF86VidModeModeLine vml;
4452                 XF86VidModeGetAllModeLines(top_level->display,
4453                         XDefaultScreen(top_level->display), &vm_count,&vm_modelines);
4454                 XF86VidModeGetModeLine(top_level->display,
4455                         XDefaultScreen(top_level->display), &dotclock,&vml);
4456                 orig_modeline.dotclock = dotclock;
4457                 orig_modeline.hdisplay = vml.hdisplay;
4458                 orig_modeline.hsyncstart = vml.hsyncstart;
4459                 orig_modeline.hsyncend = vml.hsyncend;
4460                 orig_modeline.htotal = vml.htotal;
4461                 orig_modeline.vdisplay = vml.vdisplay;
4462                 orig_modeline.vsyncstart = vml.vsyncstart;
4463                 orig_modeline.vsyncend = vml.vsyncend;
4464                 orig_modeline.vtotal = vml.vtotal;
4465                 orig_modeline.flags = vml.flags;
4466                 orig_modeline.privsize = vml.privsize;
4467                 // orig_modeline.private = vml.private;
4468                 XF86VidModeSwitchToMode(top_level->display,XDefaultScreen(top_level->display),vm_modelines[vm]);
4469                 XF86VidModeSetViewPort(top_level->display,XDefaultScreen(top_level->display),0,0);
4470                 XFlush(top_level->display);
4471         }
4472 }
4473
4474 void BC_WindowBase::restore_vm()
4475 {
4476         XF86VidModeSwitchToMode(top_level->display,XDefaultScreen(top_level->display),&orig_modeline);
4477         XFlush(top_level->display);
4478 }
4479 #endif
4480
4481
4482 #ifndef SINGLE_THREAD
4483 int BC_WindowBase::get_event_count()
4484 {
4485         event_lock->lock("BC_WindowBase::get_event_count");
4486         int result = common_events.total;
4487         event_lock->unlock();
4488         return result;
4489 }
4490
4491 XEvent* BC_WindowBase::get_event()
4492 {
4493         XEvent *result = 0;
4494         while(!done && !result)
4495         {
4496                 event_condition->lock("BC_WindowBase::get_event");
4497                 event_lock->lock("BC_WindowBase::get_event");
4498
4499                 if(common_events.total && !done)
4500                 {
4501                         result = common_events.values[0];
4502                         common_events.remove_number(0);
4503                 }
4504
4505                 event_lock->unlock();
4506         }
4507         return result;
4508 }
4509
4510 void BC_WindowBase::put_event(XEvent *event)
4511 {
4512         event_lock->lock("BC_WindowBase::put_event");
4513         common_events.append(event);
4514         event_lock->unlock();
4515         event_condition->unlock();
4516 }
4517
4518 void BC_WindowBase::dequeue_events(Window win)
4519 {
4520         event_lock->lock("BC_WindowBase::dequeue_events");
4521
4522         int out = 0, total = common_events.size();
4523         for( int in=0; in<total; ++in ) {
4524                 if( common_events[in]->xany.window == win ) continue;
4525                 common_events[out++] = common_events[in];
4526         }
4527         common_events.total = out;
4528
4529         event_lock->unlock();
4530 }
4531
4532 int BC_WindowBase::resend_event(BC_WindowBase *window)
4533 {
4534         if( resend_event_window ) return 1;
4535         resend_event_window = window;
4536         return 0;
4537 }
4538
4539 #else
4540
4541 int BC_WindowBase::resend_event(BC_WindowBase *window)
4542 {
4543         return 1;
4544 }
4545
4546 #endif // SINGLE_THREAD
4547
4548 int BC_WindowBase::get_id()
4549 {
4550         return id;
4551 }
4552
4553
4554 BC_Pixmap *BC_WindowBase::create_pixmap(VFrame *vframe)
4555 {
4556         int w = vframe->get_w(), h = vframe->get_h();
4557         BC_Pixmap *icon = new BC_Pixmap(this, w, h);
4558         icon->draw_vframe(vframe, 0,0, w,h, 0,0);
4559         return icon;
4560 }
4561
4562
4563 void BC_WindowBase::flicker(int n, int ms)
4564 {
4565         int color = get_bg_color();
4566         for( int i=2*n; --i>=0; ) {
4567                 set_inverse();          set_bg_color(WHITE);
4568                 clear_box(0,0, w,h);    flash(1);
4569                 sync_display();         Timer::delay(ms);
4570         }
4571         set_bg_color(color);
4572         set_opaque();
4573 }
4574
4575 void BC_WindowBase::focus()
4576 {
4577         XWindowAttributes xwa;
4578         XGetWindowAttributes(top_level->display, top_level->win, &xwa);
4579         if( xwa.map_state == IsViewable )
4580                 XSetInputFocus(top_level->display, top_level->win, RevertToParent, CurrentTime);
4581 }
4582