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