dlib C++ Library - gui_core_kernel_2.cpp

    // Copyright (C) 2005  Davis E. King (davis@dlib.net), Keita Mochizuki
    // License: Boost Software License   See LICENSE.txt for the full license.
    #ifndef DLIB_GUI_CORE_KERNEL_2_CPp_
    #define DLIB_GUI_CORE_KERNEL_2_CPp_
    #include "../platform.h"

    #ifdef DLIB_POSIX

    #include "gui_core_kernel_2.h"

    #include <cmath>
    #include <cstring>
    #include <iostream>
    #include <vector>
    #include <set>

    #include <X11/Xatom.h>
    #include <X11/Xlib.h>
    #include <X11/Xutil.h>
    #include <X11/keysym.h>
    #include <X11/Xlocale.h>
    #include <X11/XKBlib.h>

    #include <poll.h>

    #include "../assert.h"
    #include "../queue.h"
    #include "../sync_extension.h"
    #include "../logger.h"

    namespace dlib
    {

    // ----------------------------------------------------------------------------------------

        namespace gui_core_kernel_2_globals
        {
            void init_keyboard_mod_masks();
            struct user_event_type
            {
                Window w;
                void* p;
                int i;
            };

            typedef sync_extension<queue<user_event_type,memory_manager<char>::kernel_1b>::kernel_2a_c>::kernel_1a queue_of_user_events;

            typedef sync_extension<binary_search_tree<Window,base_window*>::kernel_1a>::kernel_1a
                window_table_type;

        // ----------------------------------------------------------------------------------------

            const std::shared_ptr<dlib::mutex>& global_mutex()
            {
                static std::shared_ptr<dlib::mutex> m(new dlib::mutex);
                return m;
            }

            class event_handler_thread : public threaded_object
            {
            public:

                enum et_state
                {
                    uninitialized,
                    initialized,
                    failure_to_init
                };

                et_state status;
                logger dlog;

                int depth;
                Display* disp;
                XIM xim;
                XIMStyle xim_style;
                Screen* screen;

                Atom delete_window;
                Window exit_window;
                std::wstring clipboard;

                int alt_mask;
                int meta_mask;
                int num_lock_mask;
                int scroll_lock_mask;

                // the mutex in this object is the global mutex used to protect everything
                // in the gui_core and gui_widgets components.
                window_table_type window_table;

                rsignaler window_close_signaler;
                rsignaler et_signaler;

                queue_of_user_events user_events;
                queue_of_user_events user_events_temp;

                std::shared_ptr<dlib::mutex> reference_to_global_mutex;

                event_handler_thread(
                ) :
                    dlog("dlib.gui_core"),
                    depth(0),
                    disp(0),
                    xim(0),
                    screen(0),
                    alt_mask(0),
                    meta_mask(0),
                    num_lock_mask(0),
                    scroll_lock_mask(0),
                    window_close_signaler(window_table.get_mutex()),
                    et_signaler(window_table.get_mutex()),
                    reference_to_global_mutex(global_mutex())
                {
                    auto_mutex M(window_table.get_mutex());

                    status = uninitialized;

                    // start up the event handler thread
                    start();

                    // wait for the event thread to get up and running
                    while (status == uninitialized)
                        et_signaler.wait();

                    if (status == failure_to_init)
                        throw gui_error("Failed to initialize X11 resources");

                    init_keyboard_mod_masks();
                }

                ~event_handler_thread ()
                {

                    if (is_alive())
                    {

                        if (status != failure_to_init)
                        {
                            XConfigureEvent event;
                            event.type = ConfigureNotify;
                            event.send_event = True;
                            event.display = disp;
                            event.window = exit_window;
                            event.x = 1;
                            XFlush(disp);
                            XPutBackEvent(disp,reinterpret_cast<XEvent*>(&event));
                            XFlush(disp);

                            // This should cause XNextEvent() to unblock so that it will see
                            // this ConfigureNotify event we are putting onto the event queue.
                            XSendEvent(disp,exit_window,False,0,reinterpret_cast<XEvent*>(&event));
                            XFlush(disp);

                            wait();

                            if (xim != NULL)
                            {
                                XCloseIM(xim);
                            }

                            XCloseDisplay(disp);

                        }
                        else
                        {

                            wait();
                        }
                    }

                }

            private:

                void thread (
                )
                {
                    using namespace std;
                    using namespace dlib;
                    try
                    {

                        // You are supposed to call this if using XLib in a threaded program.  Note
                        // however that at one point I noticed that calling this causes a dead-lock
                        // when using XIM.  But I can't reproduce that anymore and not calling it
                        // sometimes causes XCloseDisplay() to hang.
                        if (XInitThreads() == 0)
                        {
                            dlog << LFATAL << "Unable to initialize threading support.";
                            // signal that an error has occurred
                            window_table.get_mutex().lock();
                            status = failure_to_init;
                            et_signaler.broadcast();
                            window_table.get_mutex().unlock();
                            return;
                        }

                        window_table.get_mutex().lock();
                        disp = XOpenDisplay(NULL);
                        window_table.get_mutex().unlock();
                        if (disp == 0)
                        {
                            window_table.get_mutex().lock();
                            disp = XOpenDisplay(":0.0");
                            window_table.get_mutex().unlock();
                            if (disp == 0)
                            {
                                dlog << LFATAL << "Unable to connect to the X display.";
                                // signal that an error has occurred
                                window_table.get_mutex().lock();
                                status = failure_to_init;
                                et_signaler.broadcast();
                                window_table.get_mutex().unlock();
                                return;
                            }
                        }

                        window_table.get_mutex().lock();
                        screen = DefaultScreenOfDisplay(disp);
                        depth = DefaultDepthOfScreen(screen);
                        delete_window = XInternAtom(disp,"WM_DELETE_WINDOW",1);
                        window_table.get_mutex().unlock();

                        xim = NULL;
                        // I'm disabling XIM usage all together because calling XSetICValues()
                        // in set_im_pos() randomly hangs the application (on Ubuntu 13.10 at
                        // least).
                        /*
                        window_table.get_mutex().lock();
                        std::string saved_locale(setlocale (LC_CTYPE, NULL));
                        if (setlocale( LC_CTYPE, "" ) && XSupportsLocale() && XSetLocaleModifiers(""))
                            xim = XOpenIM(disp, NULL, NULL, NULL);
                        else
                            setlocale( LC_CTYPE, saved_locale.c_str() );
                        window_table.get_mutex().unlock();
                        */
                        if (xim)
                        {
                            const static XIMStyle preedit_styles[] =
                                {XIMPreeditPosition, XIMPreeditNothing, XIMPreeditNone, 0};
                            const static XIMStyle status_styles[] =
                                {XIMStatusNothing, XIMStatusNone, 0};
                            xim_style = 0;

                            XIMStyles *xim_styles;
                            window_table.get_mutex().lock();

                            XGetIMValues (xim, XNQueryInputStyle, &xim_styles, (const void*)NULL);
                            window_table.get_mutex().unlock();
                            std::set<XIMStyle> xims;
                            for (int i = 0; i < xim_styles->count_styles; ++i){
                                xims.insert(xim_styles->supported_styles[i]);
                            }
                            for (int j = 0; status_styles[j]; ++j){
                                for (int i = 0; preedit_styles[i]; ++i){
                                    xim_style = (status_styles[j] | preedit_styles[i]);
                                    if (xims.count(xim_style)) break;
                                }
                                if (xim_style) break;
                            }
                            XFree(xim_styles);
                        }

                        // make this window just so we can send messages to it and trigger
                        // events in the event thread
                        XSetWindowAttributes attr;
                        window_table.get_mutex().lock();
                        exit_window = XCreateWindow(
                            disp,
                            DefaultRootWindow(disp),
                            0,
                            0,
                            10,  // this is the default width of a window
                            10,  // this is the default width of a window
                            0,
                            depth,
                            InputOutput,
                            CopyFromParent,
                            0,
                            &attr
                        );
                        window_table.get_mutex().unlock();

                        // signal that the event thread is now up and running
                        window_table.get_mutex().lock();
                        status = initialized;
                        et_signaler.broadcast();
                        window_table.get_mutex().unlock();

                        // start the event handler
                        event_handler();
                    }
                    catch (std::exception& e)
                    {
                        cout << "\nEXCEPTION THROWN: \n" << e.what() << endl;
                        abort();
                    }
                    catch (...)
                    {
                        cout << "UNKNOWN EXCEPTION THROWN.\n" << endl;
                        abort();
                    }
                }

                void event_handler();
                void init_keyboard_mod_masks();
            };

            struct x11_base_windowstuff
            {
                Window hwnd;
                Time last_click_time;
                XIC xic;
                XFontSet fs;
                std::shared_ptr<event_handler_thread> globals;
            };

            // Do all this just to make sure global_mutex() is initialized at program start
            // and thus hopefully before any threads have the chance to startup and call
            // global_data() concurrently.
            struct call_global_mutex { call_global_mutex() { global_mutex(); } };
            static call_global_mutex call_global_mutex_instance;

            const std::shared_ptr<event_handler_thread>& global_data()
            {
                auto_mutex M(*global_mutex());
                static std::shared_ptr<event_handler_thread> p;
                if (p.get() == 0)
                    p.reset(new event_handler_thread());
                return p;
            }

        // ----------------------------------------------------------------------------------------

            Bool XCheckIfEventPredicate (
                Display* ,
                XEvent* event,
                XPointer arg
            )
            /*!
                ensures
                    - if (event is an Expose event for the window pointed to by arg) then
                        - returns true
                    - else
                        - returns false
            !*/
            {
                if (event->type == Expose)
                {
                    XExposeEvent* e = reinterpret_cast<XExposeEvent*>(event);
                    Window* win= reinterpret_cast<Window*>(arg);
                    if (e->window == *win)
                    {
                        return 1;
                    }
                }
                return 0;
            }

        // ----------------------------------------------------------------------------------------

            static bool map_keys (
                KeySym keycode,
                bool ,
                bool ,
                unsigned long& result,
                bool& is_printable
            )
            /*!
                requires
                    - if (shift was down for this key) then
                        - shift == true
                    - if (caps lock was on for this key) then
                        - caps == true
                    - keycode == the keycode from windows that we are to process
                    - keycode < keyboard_keys_size
                ensures
                    - if (this key should be ignored) then
                        - returns false
                    - else
                        - returns true
                        - #is_printable == true if result is a printable ascii character
                        - #result == the keycode converted into the proper number to tbe
                          returned by the event handler.
            !*/
            {
                is_printable = true;
                if ((keycode <= 'z' && keycode >= 'a') ||
                    (keycode <= 'Z' && keycode >= 'A') ||
                    (keycode <= '9' && keycode >= '0'))
                {
                    result = keycode;
                }
                else
                {
                    is_printable = false;
                    switch (keycode)
                    {
                    case XK_Home:   result = base_window::KEY_HOME; break;
                    case XK_Left:   result = base_window::KEY_LEFT; break;
                    case XK_Right:  result = base_window::KEY_RIGHT; break;
                    case XK_Down:   result = base_window::KEY_DOWN; break;
                    case XK_Up:     result = base_window::KEY_UP; break;
                    case XK_Prior:  result = base_window::KEY_PAGE_UP; break;
                    case XK_Next:   result = base_window::KEY_PAGE_DOWN;     break;
                    case XK_End:    result = base_window::KEY_END; break;
                    case XK_Escape:    result = base_window::KEY_ESC; break;

                    case XK_KP_Delete:    result = base_window::KEY_DELETE; break;
                    case XK_KP_Prior:    result = base_window::KEY_PAGE_UP; break;
                    case XK_KP_Next:    result = base_window::KEY_PAGE_DOWN; break;

                    case XK_F1:    result = base_window::KEY_F1; break;
                    case XK_F2:    result = base_window::KEY_F2; break;
                    case XK_F3:    result = base_window::KEY_F3; break;
                    case XK_F4:    result = base_window::KEY_F4; break;
                    case XK_F5:    result = base_window::KEY_F5; break;
                    case XK_F6:    result = base_window::KEY_F6; break;
                    case XK_F7:    result = base_window::KEY_F7; break;
                    case XK_F8:    result = base_window::KEY_F8; break;
                    case XK_F9:    result = base_window::KEY_F9; break;
                    case XK_F10:    result = base_window::KEY_F10; break;
                    case XK_F11:    result = base_window::KEY_F11; break;
                    case XK_F12:    result = base_window::KEY_F12; break;

                    case XK_Shift_L:    result = base_window::KEY_SHIFT; break;
                    case XK_Shift_R:    result = base_window::KEY_SHIFT; break;
                    case XK_Control_L:    result = base_window::KEY_CTRL; break;
                    case XK_Control_R:    result = base_window::KEY_CTRL; break;
                    case XK_Caps_Lock:    result = base_window::KEY_CAPS_LOCK; break;
                    case XK_Alt_L:    result = base_window::KEY_ALT; break;
                    case XK_Alt_R:    result = base_window::KEY_ALT; break;

                    case XK_BackSpace:    result = base_window::KEY_BACKSPACE; break;
                    case XK_Delete:    result = base_window::KEY_DELETE; break;
                    case XK_Scroll_Lock:    result = base_window::KEY_SCROLL_LOCK; break;
                    case XK_Pause:    result = base_window::KEY_PAUSE; break;
                    case XK_Insert:    result = base_window::KEY_INSERT; break;
                    case XK_KP_Insert:    result = base_window::KEY_INSERT; break;

                    case XK_exclam:
                        is_printable = true;
                        result = '!'; break;
                    case XK_quotedbl:
                        is_printable = true;
                        result = '"'; break;
                    case XK_numbersign:
                        is_printable = true;
                        result = '#'; break;
                    case XK_dollar:
                        is_printable = true;
                        result = '$'; break;
                    case XK_percent:
                        is_printable = true;
                        result = '%'; break;
                    case XK_ampersand:
                        is_printable = true;
                        result = '&'; break;
                    case XK_apostrophe:
                        is_printable = true;
                        result = '\''; break;
                    case XK_parenleft:
                        is_printable = true;
                        result = '('; break;
                    case XK_parenright:
                        is_printable = true;
                        result = ')'; break;
                    case XK_asterisk:
                        is_printable = true;
                        result = '*'; break;
                    case XK_plus:
                        is_printable = true;
                        result = '+'; break;
                    case XK_comma:
                        is_printable = true;
                        result = ','; break;
                    case XK_minus:
                        is_printable = true;
                        result = '-'; break;
                    case XK_period:
                        is_printable = true;
                        result = '.'; break;
                    case XK_slash:
                        is_printable = true;
                        result = '/'; break;
                    case XK_colon:
                        is_printable = true;
                        result = ':'; break;
                    case XK_semicolon:
                        is_printable = true;
                        result = ';'; break;
                    case XK_less:
                        is_printable = true;
                        result = '<'; break;
                    case XK_equal:
                        is_printable = true;
                        result = '='; break;
                    case XK_greater:
                        is_printable = true;
                        result = '>'; break;
                    case XK_question:
                        is_printable = true;
                        result = '?'; break;
                    case XK_at:
                        is_printable = true;
                        result = '@'; break;
                    case XK_grave:
                        is_printable = true;
                        result = '`'; break;
                    case XK_underscore:
                        is_printable = true;
                        result = '_'; break;
                    case XK_asciicircum:
                        is_printable = true;
                        result = '^'; break;
                    case XK_bracketleft:
                        is_printable = true;
                        result = '['; break;
                    case XK_backslash:
                        is_printable = true;
                        result = '\\'; break;
                    case XK_bracketright:
                        is_printable = true;
                        result = ']'; break;
                    case XK_asciitilde:
                        is_printable = true;
                        result = '~'; break;
                    case XK_braceleft:
                        is_printable = true;
                        result = '{'; break;
                    case XK_bar:
                        is_printable = true;
                        result = '|'; break;
                    case XK_braceright:
                        is_printable = true;
                        result = '}'; break;

                    case XK_space:
                        is_printable = true;
                        result = ' '; break;
                    case XK_Return:
                        is_printable = true;
                        result = '\n'; break;
                    case XK_Tab:
                        is_printable = true;
                        result = '\t'; break;
                    case XK_KP_Divide:
                        is_printable = true;
                        result = '/'; break;
                    case XK_KP_Decimal:
                        is_printable = true;
                        result = '.'; break;
                    case XK_KP_Subtract:
                        is_printable = true;
                        result = '-'; break;
                    case XK_KP_Add:
                        is_printable = true;
                        result = '+'; break;
                    case XK_KP_Multiply:
                        is_printable = true;
                        result = '*'; break;
                    case XK_KP_Equal:
                        is_printable = true;
                        result = '='; break;

                    case XK_KP_0:
                        is_printable = true;
                        result = '0'; break;
                    case XK_KP_1:
                        is_printable = true;
                        result = '1'; break;
                    case XK_KP_2:
                        is_printable = true;
                        result = '2'; break;
                    case XK_KP_3:
                        is_printable = true;
                        result = '3'; break;
                    case XK_KP_4:
                        is_printable = true;
                        result = '4'; break;
                    case XK_KP_5:
                        is_printable = true;
                        result = '5'; break;
                    case XK_KP_6:
                        is_printable = true;
                        result = '6'; break;
                    case XK_KP_7:
                        is_printable = true;
                        result = '7'; break;
                    case XK_KP_8:
                        is_printable = true;
                        result = '8'; break;
                    case XK_KP_9:
                        is_printable = true;
                        result = '9'; break;

                    default:
                        return false;
                    }
                }

                return true;
            }

        // ----------------------------------------------------------------------------------------

            void event_handler_thread::
            event_handler (
            )
            /*!
                ensures
                    - will handle all events and event dispatching
            !*/
            {
                try
                {
                    std::vector<unsigned char> bitmap_buffer;
                    bool quit_event_loop = false;
                    while (quit_event_loop == false)
                    {
                        // get a lock on the window_table's mutex
                        auto_mutex window_table_locker(window_table.get_mutex());

                        XEvent ev;
                        memset(&ev, 0, sizeof(ev));
                        while (XPending(disp) == 0){
                            window_table.get_mutex().unlock();
                            // wait until receiving X11 next event
                            struct pollfd pfd;
                            pfd.fd = ConnectionNumber(disp);
                            pfd.events = POLLIN | POLLPRI;
                            poll(&pfd, 1, -1);

                            window_table.get_mutex().lock();
                        }
                        XNextEvent(disp,&ev);

                        // pass events to input method.
                        // if this event is needed by input method, XFilterEvent returns True
                        if (XFilterEvent(&ev, None) == True){
                            continue;
                        }

                        // if this event is for one of the windows in the window_table
                        // then get that window out of the table and put it into win.
                        XAnyEvent* _ae = reinterpret_cast<XAnyEvent*>(&ev);
                        base_window** win_ = window_table[_ae->window];
                        base_window* win = 0;
                        if (win_)
                            win = *win_;

                        // ignore messages for unmapped windows
                        if (ev.type != MapNotify && win != 0)
                        {
                            if (win->is_mapped == false)
                               continue;
                        }

                        switch (ev.type)
                        {

                        case SelectionRequest:
                            {
                                Atom a_ct = XInternAtom(disp, "COMPOUND_TEXT", False);
                                XSelectionRequestEvent* req = reinterpret_cast<XSelectionRequestEvent*>(&ev.xselectionrequest);
                                XEvent respond;

                                if (req->target == XA_STRING)
                                {
                                    XChangeProperty (disp,
                                                     req->requestor,
                                                     req->property,
                                                     XA_STRING,
                                                     8,
                                                     PropModeReplace,
                                                     reinterpret_cast<const unsigned char*>(convert_wstring_to_mbstring(clipboard).c_str()),
                                                     clipboard.size()+1);
                                    respond.xselection.property=req->property;
                                }
                                else if (req->target == a_ct)
                                {
                                    XChangeProperty (disp,
                                                     req->requestor,
                                                     req->property,
                                                     a_ct,
                                                     sizeof(wchar_t)*8,
                                                     PropModeReplace,
                                                     reinterpret_cast<const unsigned char*>(clipboard.c_str()),
                                                     clipboard.size()+1);
                                    respond.xselection.property=req->property;
                                }
                                else
                                {
                                    respond.xselection.property= None;
                                }
                                respond.xselection.type= SelectionNotify;
                                respond.xselection.display= req->display;
                                respond.xselection.requestor= req->requestor;
                                respond.xselection.selection=req->selection;
                                respond.xselection.target= req->target;
                                respond.xselection.time = req->time;
                                XSendEvent (disp, req->requestor,0,0,&respond);
                                XFlush (disp);

                            } break;

                        case MapNotify:
                            {
                                if (win == 0)
                                    break;

                                win->is_mapped = true;

                                if (win->resizable == false)
                                {
                                    XSizeHints* hints = XAllocSizeHints();
                                    hints->flags = PMinSize|PMaxSize;
                                    hints->min_width = win->width;
                                    hints->max_width = win->width;
                                    hints->max_height = win->height;
                                    hints->min_height = win->height;
                                    XSetNormalHints(disp,win->x11_stuff.hwnd,hints);
                                    XFree(hints);
                                }

                                if (win->has_been_resized)
                                {
                                    XResizeWindow(disp,win->x11_stuff.hwnd,win->width,win->height);
                                    win->has_been_resized = false;
                                    win->on_window_resized();
                                }

                                if (win->has_been_moved)
                                {
                                    XMoveWindow(disp,win->x11_stuff.hwnd,win->x,win->y);
                                    win->has_been_moved = false;
                                    win->on_window_moved();
                                }
                                XFlush(disp);

                            } break;

                        case KeyPress:
                            {
                                XKeyPressedEvent* e = reinterpret_cast<XKeyPressedEvent*>(&ev);

                                if (win == 0)
                                    break;

                                unsigned long state = 0;
                                bool shift = ((e->state & ShiftMask)!=0);
                                bool ctrl = ((e->state & ControlMask)!=0);
                                bool caps = ((e->state & LockMask)!=0);
                                if(shift)
                                    state |= base_window::KBD_MOD_SHIFT;
                                if(ctrl)
                                    state |= base_window::KBD_MOD_CONTROL;
                                if(caps)
                                    state |= base_window::KBD_MOD_CAPS_LOCK;
                                if((e->state & alt_mask)!=0)
                                    state |= base_window::KBD_MOD_ALT;
                                if((e->state & meta_mask)!=0)
                                    state |= base_window::KBD_MOD_META;
                                if((e->state & num_lock_mask)!=0)
                                    state |= base_window::KBD_MOD_NUM_LOCK;
                                if((e->state & scroll_lock_mask)!=0)
                                    state |= base_window::KBD_MOD_SCROLL_LOCK;

                                KeySym key;
                                Status status;

                                if (win->x11_stuff.xic) {
                                    std::wstring wstr;
                                    wstr.resize(2);
                                    int len = XwcLookupString(win->x11_stuff.xic,e,&wstr[0],wstr.size(),&key,&status);
                                    if (status == XBufferOverflow){
                                        wstr.resize(len);
                                        len = XwcLookupString(win->x11_stuff.xic,e,&wstr[0],wstr.size(),&key,&status);
                                    }
                                    if (status == XLookupChars){
                                        win->on_string_put(wstr);
                                    }
                                } else {
                                    char buffer[2];
                                    XLookupString(e, buffer, sizeof(buffer), &key, NULL);
                                    status = XLookupKeySym;
                                }

                                if (status == XLookupKeySym || status == XLookupBoth){

                                    bool is_printable;
                                    unsigned long result;

                                    if (map_keys(key,shift,caps,result,is_printable))
                                    {
                                        // signal the keyboard event
                                        win->on_keydown(result,is_printable,state);
                                    }
                                }

                            } break;

                        case FocusIn:
                            {
                                if (win == 0)
                                    break;

                                // signal the focus event
                                win->on_focus_gained();
                            } break;

                        case FocusOut:
                            {
                                if (win == 0)
                                    break;

                                // signal the focus event
                                win->on_focus_lost();
                            } break;

                        case ButtonPress:
                        case ButtonRelease:
                            {
                                XButtonEvent* e = reinterpret_cast<XButtonEvent*>(&ev);

                                if (win == 0)
                                    break;

                                unsigned long btn = base_window::NONE;
                                if (e->button == Button1)
                                    btn = base_window::LEFT;
                                else if (e->button == Button3)
                                    btn = base_window::RIGHT;
                                else if (e->button == Button2)
                                    btn = base_window::MIDDLE;

                                unsigned long state = 0;
                                if (e->state & ControlMask)
                                    state |= base_window::CONTROL;
                                if (e->state & Button1Mask)
                                    state |= base_window::LEFT;
                                if (e->state & Button2Mask)
                                    state |= base_window::MIDDLE;
                                if (e->state & Button3Mask)
                                    state |= base_window::RIGHT;
                                if (e->state & ShiftMask)
                                    state |= base_window::SHIFT;

                                // only send the event if this is a button we support
                                if (btn != (unsigned long)base_window::NONE)
                                {

                                    if (ev.type == ButtonPress)
                                    {
                                        bool is_double_click = false;
                                        if (win->last_click_button == btn &&
                                            std::abs((long)win->last_click_x - (long)e->x) < 5 &&
                                            std::abs((long)win->last_click_y - (long)e->y) < 5 &&
                                            e->time - win->x11_stuff.last_click_time <= 400)
                                        {
                                            // this is a double click
                                            is_double_click = true;
                                            // set this to make sure the next click can't be
                                            // interpreted as a double click
                                            win->last_click_button = base_window::NONE;
                                        }
                                        else
                                        {
                                            win->last_click_button = btn;
                                            win->last_click_x = e->x;
                                            win->last_click_y = e->y;
                                            win->x11_stuff.last_click_time = e->time;
                                        }

                                        // remove the clicked button from the state
                                        state &= (~btn);
                                        win->on_mouse_down(btn,state,e->x,e->y,is_double_click);

                                    }
                                    else
                                    {
                                        // remove the clicked button from the state
                                        state &= (~btn);
                                        win->on_mouse_up(btn,state,e->x,e->y);
                                    }
                                }
                                else if (e->button == Button4 && ev.type == ButtonPress)
                                {
                                    win->on_wheel_up(state);
                                }
                                else if (e->button == Button5 && ev.type == ButtonPress)
                                {
                                    win->on_wheel_down(state);
                                }

                            } break;

                        case LeaveNotify:
                            {
                                if (win == 0)
                                    break;

                                win->on_mouse_leave();

                            } break;

                        case EnterNotify:
                            {
                                if (win == 0)
                                    break;

                                win->on_mouse_enter();
                            } break;

                        case MotionNotify:
                            {
                                XMotionEvent* e = reinterpret_cast<XMotionEvent*>(&ev);

                                if (win == 0)
                                    break;

                                unsigned long state = 0;
                                if (e->state & ControlMask)
                                    state |= base_window::CONTROL;
                                if (e->state & Button1Mask)
                                    state |= base_window::LEFT;
                                if (e->state & Button2Mask)
                                    state |= base_window::MIDDLE;
                                if (e->state & Button3Mask)
                                    state |= base_window::RIGHT;
                                if (e->state & ShiftMask)
                                    state |= base_window::SHIFT;

                                win->on_mouse_move(state,e->x,e->y);

                            } break;

                        case ConfigureNotify:
                            {
                                XConfigureEvent* e = reinterpret_cast<XConfigureEvent*>(&ev);
                                if (e->window == exit_window)
                                {
                                    // this is the signal to quit the event handler
                                    quit_event_loop = true;
                                    break;
                                }

                                if (win == 0)
                                    break;

                                if (win->width != e->width ||
                                    win->height != e->height ||
                                    win->has_been_resized)
                                {
                                    win->has_been_resized = false;
                                    // this is a resize
                                    win->width = e->width;
                                    win->height = e->height;
                                    win->on_window_resized();
                                }
                                if (win->x != e->x ||
                                    win->y != e->y ||
                                    win->has_been_moved)
                                {
                                    win->has_been_moved = false;
                                    // this is a move
                                    win->x = e->x;
                                    win->y = e->y;
                                    win->on_window_moved();
                                }

                            } break;

                        case ClientMessage:
                            {
                                XClientMessageEvent* e = reinterpret_cast<XClientMessageEvent*>(&ev);
                                if ((Atom)e->data.l[0] == delete_window)
                                {
                                    if (win == 0)
                                        break;

                                    if (win->on_window_close() == base_window::DO_NOT_CLOSE_WINDOW)
                                    {
                                        DLIB_ASSERT(win->has_been_destroyed == false,
                                            "\tYou called close_window() inside the on_window_close() event but"
                                            << "\n\tthen returned DO_NOT_CLOSE_WINDOW.  You can do one or the other but not both."
                                            << "\n\tthis:     " << win
                                            );
                                        // the client has decided not to close the window
                                        // after all
                                    }
                                    else
                                    {
                                        if (window_table[e->window])
                                        {
                                            window_table.destroy(e->window);
                                            XDestroyWindow(disp,e->window);
                                            win->has_been_destroyed = true;
                                            window_close_signaler.broadcast();
                                        }
                                        else
                                        {
                                            // in this case the window must have self destructed by
                                            // calling delete this;  so we don't have to do anything.
                                        }
                                    }
                                }
                            } break;

                        case Expose:
                            {
                                XExposeEvent* e = reinterpret_cast<XExposeEvent*>(&ev);

                                if (win == 0)
                                    break;

                                // take all the expose events for this window out
                                XEvent etemp;
                                int x = e->x;
                                int y = e->y;
                                int width = e->width;
                                int height = e->height;

                                // What we are doing here with this loop is we are combining
                                // all of the Expose events for this window that are
                                // currently in the queue.
                                while (XCheckIfEvent(disp,&etemp,XCheckIfEventPredicate,reinterpret_cast<XPointer>(&(e->window))))
                                {
                                    XExposeEvent* e2 = reinterpret_cast<XExposeEvent*>(&etemp);
                                    if (e2->x < x)
                                    {
                                        width += x - e2->x;
                                        x = e2->x;
                                    }
                                    if (e2->y < y)
                                    {
                                        height += y - e2->y;
                                        y = e2->y;
                                    }
                                    if (e2->width + e2->x > width + x)
                                    {
                                        width = e2->width + e2->x - x;
                                    }
                                    if (e2->height + e2->y > height + y)
                                    {
                                        height = e2->height + e2->y - y;
                                    }
                                }

                                // I'm not sure if this sort of thing can happen but
                                // if it does then just ignore this entire event.
                                if (width == 0 || height == 0)
                                {
                                    break;
                                }

                                if (bitmap_buffer.size() < static_cast<unsigned long>(width*height*4))
                                    bitmap_buffer.resize(width*height*4);

                                unsigned char* const bitmap = &bitmap_buffer[0];
                                unsigned char* const end = bitmap + width*height*4;

                                unsigned char* temp;
                                canvas c(bitmap,x,y,x+width-1,y+height-1);

                                win->paint(c);

                                // the user might have called win->close_window() and if they did
                                // then just stop right here.  We don't want to paint the window.
                                if (win->has_been_destroyed)
                                    break;

                                // if the color depth we are working with isn't 24bits then we need
                                // to transform our image into whatever it is supposed to be.
                                if (depth != 24)
                                {
                                    // convert this image into an 8 bit image
                                    unsigned int red_bits = 0;
                                    unsigned int green_bits = 0;
                                    unsigned int blue_bits = 0;
                                    if (depth != 16)
                                    {
                                        unsigned int bits = depth/3;
                                        unsigned int extra = depth%3;
                                        red_bits = bits;
                                        green_bits = bits;
                                        blue_bits = bits;
                                        if (extra)
                                        {
                                            ++red_bits;
                                            --extra;
                                        }
                                        if (extra)
                                        {
                                            ++green_bits;
                                        }
                                    }
                                    else if (depth == 16)
                                    {
                                        red_bits = 5;
                                        green_bits = 6;
                                        blue_bits = 5;
                                    }

                                    if (depth == 16)
                                    {
                                        temp = bitmap;
                                        unsigned char *red, *green, *blue;
                                        while (temp != end)
                                        {
                                            blue = temp;
                                            ++temp;
                                            green = temp;
                                            ++temp;
                                            red = temp;
                                            ++temp;
                                            ++temp;

                                            const unsigned long r = static_cast<unsigned long>(*red)>>(8-red_bits);
                                            const unsigned long g = static_cast<unsigned long>(*green)>>(8-green_bits);
                                            const unsigned long b = static_cast<unsigned long>(*blue)>>(8-blue_bits);

                                            unsigned long color = (r<<(depth-red_bits))| (g<<(depth-red_bits-green_bits))| b;

                                            *blue  = (color>>0)&0xFF;
                                            *green = (color>>8)&0xFF;
                                        }
                                    }
                                    else if (depth < 24)
                                    {
                                        temp = bitmap;
                                        unsigned char *red, *green, *blue;
                                        while (temp != end)
                                        {
                                            blue = temp;
                                            ++temp;
                                            green = temp;
                                            ++temp;
                                            red = temp;
                                            ++temp;
                                            ++temp;

                                            const unsigned long r = static_cast<unsigned long>(*red)>>(8-red_bits);
                                            const unsigned long g = static_cast<unsigned long>(*green)>>(8-green_bits);
                                            const unsigned long b = static_cast<unsigned long>(*blue)>>(8-blue_bits);

                                            unsigned long color = (b<<(depth-blue_bits))| (g<<(depth-blue_bits-green_bits))| r;

                                            *blue  = (color>>0)&0xFF;
                                            *green = (color>>8)&0xFF;
                                            *red   = (color>>16)&0xFF;
                                        }
                                    }
                                    else if (depth > 24)
                                    {
                                        temp = bitmap;
                                        unsigned char *red, *green, *blue, *four;
                                        while (temp != end)
                                        {
                                            blue = temp;
                                            ++temp;
                                            green = temp;
                                            ++temp;
                                            red = temp;
                                            ++temp;
                                            four = temp;
                                            ++temp;

                                            const unsigned long r = static_cast<unsigned long>(*red)<<(red_bits-8);
                                            const unsigned long g = static_cast<unsigned long>(*green)<<(green_bits-8);
                                            const unsigned long b = static_cast<unsigned long>(*blue)<<(blue_bits-8);

                                            unsigned long color = (b<<(depth-blue_bits))| (g<<(depth-blue_bits-green_bits))| r;

                                            *blue  = (color>>0)&0xFF;
                                            *green = (color>>8)&0xFF;
                                            *red   = (color>>16)&0xFF;
                                            *four  = (color>>24)&0xFF;
                                        }
                                    }
                                } // if (depth != 24)

                                XImage img;
                                memset(&img,0,sizeof(img));
                                img.width = width;
                                img.height = height;
                                img.depth = depth;
                                img.data = reinterpret_cast<char*>(bitmap);
                                img.bitmap_bit_order = LSBFirst;
                                img.byte_order = LSBFirst;
                                img.format = ZPixmap;
                                img.bitmap_pad = 32;
                                img.bitmap_unit = 32;
                                img.bits_per_pixel = 32;

                                XInitImage(&img);

                                GC gc = XCreateGC(disp, e->window, 0, NULL);

                                XPutImage(disp,e->window,gc,&img,0,0,x,y,width,height);

                                XFreeGC(disp,gc);
                            } break;
                        } // switch (ev.type)
                    }
                }
                catch (std::exception& e)
                {
                    dlog << LFATAL << "Exception thrown in event handler: " << e.what();
                }
                catch (...)
                {
                    dlog << LFATAL << "Unknown exception thrown in event handler.";
                }
            }

        // ----------------------------------------------------------------------------------------

            int index_to_modmask(unsigned long n)
            {
                switch ( n )
                {
                    case 0:
                        return Mod1Mask;
                    case 1:
                        return Mod2Mask;
                    case 2:
                        return Mod3Mask;
                    case 3:
                        return Mod4Mask;
                }
                return Mod5Mask;
            }

            void event_handler_thread::
            init_keyboard_mod_masks()
            {
                XModifierKeymap* map = XGetModifierMapping( disp );
                KeyCode* codes = map->modifiermap + map->max_keypermod * Mod1MapIndex;
                for (int n = 0; n < 5 * map->max_keypermod; n++ )
                {
                    if ( codes[n] == 0 )
                        continue;
                    switch(XkbKeycodeToKeysym( disp, codes[n], 0, 0 ))
                    {
                        case XK_Alt_L:
                            alt_mask = index_to_modmask(n / map->max_keypermod);
                            continue;
                        case XK_Alt_R:
                            if(alt_mask == 0)
                                alt_mask = index_to_modmask(n / map->max_keypermod);
                            continue;
                        case XK_Meta_L:
                        case XK_Meta_R:
                            meta_mask = index_to_modmask(n / map->max_keypermod);
                            continue;
                        case XK_Scroll_Lock:
                            scroll_lock_mask = index_to_modmask(n / map->max_keypermod);
                            continue;
                        case XK_Num_Lock:
                            num_lock_mask = index_to_modmask(n / map->max_keypermod);
                        default:
                            continue;
                    }
                }
                XFreeModifiermap( map );
                if ( alt_mask == 0 )
                {
                    dlog << LWARN << "Search for Alt-key faild.";
                    if ( meta_mask != 0 )
                        alt_mask = meta_mask;
                    else
                        alt_mask = Mod1Mask; // resort to guessing
                }
            }

        // ----------------------------------------------------------------------------------------

        } // namespace gui_core_kernel_2_globals

    // ----------------------------------------------------------------------------------------

        void canvas::
        fill (
            unsigned char red_,
            unsigned char green_,
            unsigned char blue_
        ) const
        {
            pixel pixel_value;
            pixel_value.red = red_;
            pixel_value.green = green_;
            pixel_value.blue = blue_;
            pixel_value._padding = 0;

            pixel* start = reinterpret_cast<pixel*>(bits);
            pixel* end = start + width_*height_;

            while (start != end)
            {
                *start = pixel_value;
                ++start;
            }
        }

    // ----------------------------------------------------------------------------------------

        void put_on_clipboard (
            const std::string& str
        )
        {
            put_on_clipboard(convert_mbstring_to_wstring(str));
        }

        void put_on_clipboard (
            const dlib::ustring& str
        )
        {
            put_on_clipboard(convert_utf32_to_wstring(str));
        }

        void put_on_clipboard (
            const std::wstring& str
        )
        {
            using namespace gui_core_kernel_2_globals;

            std::shared_ptr<event_handler_thread> globals(global_data());

            auto_mutex M(globals->window_table.get_mutex());
            globals->clipboard = str.c_str();

            XSetSelectionOwner(globals->disp,XA_PRIMARY,globals->exit_window,CurrentTime);
        }

    // ----------------------------------------------------------------------------------------

        Bool clip_peek_helper (
            Display*,
            XEvent* event,
            XPointer
        )
        {
            if ( event->type == SelectionNotify)
            {
                return True;
            }
            else
            {
                return False;
            }
        }

        void get_from_clipboard (
            std::string& str
        )
        {
            std::wstring wstr;
            get_from_clipboard(wstr);
            str = convert_wstring_to_mbstring(wstr);
        }

        void get_from_clipboard (
            dlib::ustring& str
        )
        {
            std::wstring wstr;
            get_from_clipboard(wstr);
            str = convert_to_utf32(wstr);
        }

        void get_from_clipboard (
            std::wstring& str
        )
        {
            using namespace gui_core_kernel_2_globals;
            std::shared_ptr<event_handler_thread> globals(global_data());

            auto_mutex M(globals->window_table.get_mutex());
            str.clear();
            unsigned char *data = 0;
            wchar_t **plist = 0;
            Window sown;
            Atom  type;
            int format, result;
            unsigned long len, bytes_left, dummy;
            XEvent e;

            try
            {
                Atom atom_ct = XInternAtom(globals->disp, "COMPOUND_TEXT", False);
                sown = XGetSelectionOwner (globals->disp, XA_PRIMARY);
                if (sown == globals->exit_window)
                {
                    // if we are copying from ourselfs then don't fool with the Xwindows junk.
                    str = globals->clipboard.c_str();
                }
                else if (sown != None)
                {
                    // request that the selection be copied into the XA_PRIMARY property
                    // of the exit_window.  It doesn't matter what window we put it in
                    // so long as it is one under the control of this process and exit_window
                    // is easy to use here so that is what I'm using.
                    XConvertSelection (globals->disp, XA_PRIMARY, atom_ct, XA_PRIMARY,
                                       globals->exit_window, CurrentTime);

                    // This will wait until we get a SelectionNotify event which should happen
                    // really soon.
                    XPeekIfEvent(globals->disp,&e,clip_peek_helper,0);

                    // See how much data we got
                    XGetWindowProperty (globals->disp, globals->exit_window,
                                        XA_PRIMARY,    // Tricky..
                                        0, 0,         // offset - len
                                        0,        // Delete 0==FALSE
                                        AnyPropertyType,  //flag
                                        &type,        // return type
                                        &format,      // return format
                                        &len, &bytes_left,  //that
                                        &data);
                    if (data)
                    {
                        XFree(data);
                        data = 0;
                    }
                    if (bytes_left > 0 && type == atom_ct)
                    {
                        XTextProperty p;
                        result = XGetWindowProperty (globals->disp, globals->exit_window,
                                                     XA_PRIMARY, 0,bytes_left,0,
                                                     AnyPropertyType, &p.encoding,&p.format,
                                                     &p.nitems, &dummy, &p.value);
                        if (result == Success && p.encoding == atom_ct)
                        {
                            int n;
                            XwcTextPropertyToTextList(globals->disp, &p, &plist, &n);
                            str = plist[0];
                        }
                        if (plist)
                        {
                            XwcFreeStringList(plist);
                            plist = 0;
                        }
                    }
                }
            }
            catch (...)
            {
                if (data)
                    XFree(data);
                if (plist)
                {
                    XwcFreeStringList(plist);
                    plist = 0;
                }
            }
        }

    // ----------------------------------------------------------------------------------------

        namespace gui_core_kernel_2_globals
        {
            void trigger_user_event_threadproc (
                void*
            )
            {
                std::shared_ptr<event_handler_thread> globals(global_data());
                auto_mutex M(globals->window_table.get_mutex());

                globals->user_events.lock();
                globals->user_events.swap(globals->user_events_temp);
                globals->user_events.unlock();

                globals->user_events_temp.reset();
                // now dispatch all these user events
                while (globals->user_events_temp.move_next())
                {
                    base_window** win_ = globals->window_table[globals->user_events_temp.element().w];
                    base_window* win;
                    // if this window exists in the window table then dispatch
                    // its event.
                    if (win_)
                    {
                        win = *win_;
                        win->on_user_event(
                            globals->user_events_temp.element().p,
                            globals->user_events_temp.element().i
                        );
                    }
                }
                globals->user_events_temp.clear();
            }
        }

        void base_window::
        trigger_user_event (
            void* p,
            int i
        )
        {
            using namespace gui_core_kernel_2_globals;
            user_event_type e;
            e.w = x11_stuff.hwnd;
            e.p = p;
            e.i = i;
            {
                std::shared_ptr<event_handler_thread> globals(global_data());
                auto_mutex M(globals->user_events.get_mutex());
                globals->user_events.enqueue(e);

                // we only need to start a thread to deal with this if there isn't already
                // one out working on the queue
                if (globals->user_events.size() == 1)
                    create_new_thread (trigger_user_event_threadproc,0);
            }
        }

    // ----------------------------------------------------------------------------------------

        base_window::
        base_window (
            bool resizable_,
            bool undecorated
        ) :
            x11_stuff(*(new gui_core_kernel_2_globals::x11_base_windowstuff)),
            is_mapped(false),
            resizable(resizable_),
            has_been_destroyed(false),
            has_been_resized(false),
            has_been_moved(false),
            wm(gui_core_kernel_2_globals::global_data()->window_table.get_mutex())
        {
            DLIB_ASSERT(!(undecorated == true && resizable_ == true),
                "\tbase_window::base_window()"
                << "\n\tThere is no such thing as an undecorated window that is resizable by the user."
                << "\n\tthis:     " << this
                );
            using namespace gui_core_kernel_2_globals;

            auto_mutex M(wm);

            x11_stuff.globals = global_data();

            x11_stuff.last_click_time = 0;
            last_click_x = 0;
            last_click_y = 0;
            last_click_button = NONE;

            XSetWindowAttributes attr;
            memset(&attr,'\0',sizeof(attr));

            unsigned long valuemask = 0;
            if (undecorated)
            {
                attr.override_redirect = True;
                valuemask = CWOverrideRedirect;
            }

            x11_stuff.hwnd = XCreateWindow(
                            x11_stuff.globals->disp,
                            DefaultRootWindow(x11_stuff.globals->disp),
                            0,
                            0,
                            10,  // this is the default width of a window
                            10,  // this is the default width of a window
                            0,
                            x11_stuff.globals->depth,
                            InputOutput,
                            CopyFromParent,
                            valuemask,
                            &attr
                            );

            x11_stuff.xic = NULL;
            if (x11_stuff.globals->xim)
            {
                XVaNestedList   xva_nlist;
                XPoint          xpoint;

                char **mlist;
                int mcount;
                char *def_str;
                char fontset[256] = {0};
                const long native_font_height = 12;
                snprintf(fontset, sizeof(fontset), "-*-*-medium-r-normal--%lu-*-*-*-", native_font_height);
                x11_stuff.fs = XCreateFontSet(x11_stuff.globals->disp, fontset, &mlist, &mcount, &def_str);
                xpoint.x = 0;
                xpoint.y = 0;
                xva_nlist = XVaCreateNestedList(0, XNSpotLocation, &xpoint, XNFontSet, x11_stuff.fs, (const void*)NULL);
                x11_stuff.xic = XCreateIC(
                    x11_stuff.globals->xim,
                    XNInputStyle, x11_stuff.globals->xim_style,
                    XNClientWindow, x11_stuff.hwnd,
                    XNPreeditAttributes, xva_nlist,
                    (const void*)NULL
                    );
                XFree(xva_nlist);
                XFreeStringList(mlist);
            }

            Window temp = x11_stuff.hwnd;
            base_window* ttemp = this;
            x11_stuff.globals->window_table.add(temp,ttemp);

            // query event mask required by input method
            unsigned long event_xim = 0;
            if (x11_stuff.xic)
                 XGetICValues( x11_stuff.xic, XNFilterEvents, &event_xim, (const void*)NULL );

            XSelectInput(
                x11_stuff.globals->disp,
                x11_stuff.hwnd,
                StructureNotifyMask|ExposureMask|ButtonPressMask|ButtonReleaseMask|
                PointerMotionMask|LeaveWindowMask|EnterWindowMask|KeyPressMask|
                KeyReleaseMask| FocusChangeMask | event_xim
                );

            XSetWMProtocols(
                x11_stuff.globals->disp,
                x11_stuff.hwnd,
                &x11_stuff.globals->delete_window,
                1
                );

            // these are just default values
            x = 0;
            y = 0;
            width = 10;
            height = 10;

            if (resizable == false)
            {
                XSizeHints* hints = XAllocSizeHints();
                hints->flags = PMinSize|PMaxSize;
                hints->min_width = width;
                hints->max_width = width;
                hints->max_height = height;
                hints->min_height = height;
                XSetNormalHints(x11_stuff.globals->disp,x11_stuff.hwnd,hints);
                XFree(hints);
            }
        }

    // ----------------------------------------------------------------------------------------

        base_window::
        ~base_window (
        )
        {
            using namespace gui_core_kernel_2_globals;
            close_window();

            if (x11_stuff.globals->xim != NULL)
            {
                XDestroyIC(x11_stuff.xic);
                x11_stuff.xic = 0;
                XFreeFontSet(x11_stuff.globals->disp,x11_stuff.fs);
            }

            delete &x11_stuff;
        }

    // ----------------------------------------------------------------------------------------

        void base_window::
        close_window (
        )
        {
            using namespace gui_core_kernel_2_globals;
            auto_mutex M(wm);
            if (has_been_destroyed == false)
            {
                has_been_destroyed = true;

                x11_stuff.globals->window_table.destroy(x11_stuff.hwnd);

                XDestroyWindow(x11_stuff.globals->disp,x11_stuff.hwnd);
                x11_stuff.hwnd = 0;
                x11_stuff.globals->window_close_signaler.broadcast();
            }
        }

    // ----------------------------------------------------------------------------------------

        bool base_window::
        is_closed (
        ) const
        {
            auto_mutex M(wm);
            return has_been_destroyed;
        }

    // ----------------------------------------------------------------------------------------

        void base_window::
        set_title (
            const std::string& title_
        )
        {
            set_title(convert_mbstring_to_wstring(title_));
        }

        void base_window::
        set_title (
            const ustring& title_
        )
        {
            set_title(convert_utf32_to_wstring(title_));
        }

        void base_window::
        set_title (
            const std::wstring& title_
        )
        {
            using namespace gui_core_kernel_2_globals;
            auto_mutex M(wm);
            if (has_been_destroyed == true)
                return;

            // I'm pretty sure the pointer won't be modified even though
            // it isn't const anymore.
            wchar_t *title = const_cast<wchar_t *>(title_.c_str());
            XTextProperty property;
            int rc = XwcTextListToTextProperty(x11_stuff.globals->disp,&title,1,XStdICCTextStyle, &property);
            if (rc >= 0) {
                XSetWMName(x11_stuff.globals->disp,x11_stuff.hwnd,&property);
                XFree(property.value);
                XFlush(x11_stuff.globals->disp);
            }
        }

    // ----------------------------------------------------------------------------------------

        void base_window::
        show (
        )
        {
            using namespace gui_core_kernel_2_globals;
            auto_mutex M(wm);
            if (has_been_destroyed == true)
                return;

            XMapRaised(x11_stuff.globals->disp,x11_stuff.hwnd);
            XFlush(x11_stuff.globals->disp);
        }

    // ----------------------------------------------------------------------------------------

        void base_window::
        wait_until_closed (
        ) const
        {
            using namespace gui_core_kernel_2_globals;
            auto_mutex M(wm);
            while (has_been_destroyed == false)
                x11_stuff.globals->window_close_signaler.wait();
        }

    // ----------------------------------------------------------------------------------------

        void base_window::
        hide (
        )
        {
            using namespace gui_core_kernel_2_globals;
            auto_mutex M(wm);
            if (has_been_destroyed == true)
                return;

            XUnmapWindow(x11_stuff.globals->disp,x11_stuff.hwnd);
            XFlush(x11_stuff.globals->disp);
        }

    // ----------------------------------------------------------------------------------------

        void base_window::
        set_size (
            int width_,
            int height_
        )
        {
            using namespace gui_core_kernel_2_globals;
            auto_mutex a(wm);
            if (has_been_destroyed == true)
                return;

            // do some sanity checking on these values
            if (width_ < 1)
                width_ = 1;
            if (height_ < 1)
                height_ = 1;

            width = width_;
            height = height_;
            has_been_resized = true;

            if (resizable == false)
            {
                XSizeHints* hints = XAllocSizeHints();
                hints->flags = PMinSize|PMaxSize;
                hints->min_width = width;
                hints->max_width = width;
                hints->max_height = height;
                hints->min_height = height;
                XSetNormalHints(x11_stuff.globals->disp,x11_stuff.hwnd,hints);
                XFree(hints);
            }

            XResizeWindow(x11_stuff.globals->disp,x11_stuff.hwnd,width,height);

            XFlush(x11_stuff.globals->disp);
        }

    // ----------------------------------------------------------------------------------------

        void base_window::
        set_pos (
            long x_,
            long y_
        )
        {
            using namespace gui_core_kernel_2_globals;
            auto_mutex a(wm);
            if (has_been_destroyed == true)
                return;

            x = x_;
            y = y_;

            has_been_moved = true;

            XMoveWindow(x11_stuff.globals->disp,x11_stuff.hwnd,x,y);
            XFlush(x11_stuff.globals->disp);
        }

    // ----------------------------------------------------------------------------------------

        void base_window::
        get_pos (
            long& x_,
            long& y_
        )
        {
            using namespace gui_core_kernel_2_globals;
            auto_mutex a(wm);
            x_ = 0;
            y_ = 0;
            if (has_been_destroyed == true)
                return;

            // we can't really trust the values we have for x and y because some window managers
            // will have reported bogus values back in the ConfigureNotify event.  So just to be
            // on the safe side we will use XTranslateCoordinates()
            int rx, ry;
            Window desktop_window = DefaultRootWindow(x11_stuff.globals->disp);
            Window junk;
            XTranslateCoordinates(x11_stuff.globals->disp,x11_stuff.hwnd,desktop_window,0,0,&rx, &ry, &junk);
            x_ = rx;
            y_ = ry;
            x = rx;
            y = ry;
        }

    // ----------------------------------------------------------------------------------------

        void base_window::
        get_size (
            unsigned long& width_,
            unsigned long& height_
        ) const
        {
            auto_mutex M(wm);
            width_ = 0;
            height_ = 0;
            if (has_been_destroyed == true)
                return;

            width_ = width;
            height_ = height;
        }

    // ----------------------------------------------------------------------------------------

        void base_window::
        get_display_size (
            unsigned long& width_,
            unsigned long& height_
        ) const
        {
            using namespace gui_core_kernel_2_globals;
            auto_mutex M(wm);
            width_ = 0;
            height_ = 0;
            if (has_been_destroyed == true)
                return;

            int screen_number = XScreenNumberOfScreen(x11_stuff.globals->screen);
            width_ = DisplayWidth(x11_stuff.globals->disp, screen_number);
            height_ = DisplayHeight(x11_stuff.globals->disp, screen_number);
        }

    // ----------------------------------------------------------------------------------------

        void base_window::
        invalidate_rectangle (
            const rectangle& rect
        )
        {
            using namespace gui_core_kernel_2_globals;
            auto_mutex a(wm);
            if (is_mapped == false)
                return;

            if (rect.is_empty() == false && !has_been_destroyed)
            {
                const long x = rect.left();
                const long y = rect.top();
                const unsigned long width = rect.width();
                const unsigned long height = rect.height();

                XClearArea(x11_stuff.globals->disp,x11_stuff.hwnd,x,y,width,height,1);
                XFlush(x11_stuff.globals->disp);
            }
        }

    // ----------------------------------------------------------------------------------------

        void base_window::
        set_im_pos (
            long x,
            long y
        )
        {
            using namespace gui_core_kernel_2_globals;
            auto_mutex a(wm);
            if (has_been_destroyed == true)
                return;

            if (!x11_stuff.xic || !(x11_stuff.globals->xim_style & XIMPreeditPosition)) return;

            XVaNestedList   xva_nlist;
            XPoint          xpoint;

            xpoint.x = x;
            xpoint.y = y;

            xva_nlist = XVaCreateNestedList(0, XNSpotLocation, &xpoint, (const void*)NULL);
            XSetICValues(x11_stuff.xic, XNPreeditAttributes, xva_nlist, (const void*)NULL);
            XFree(xva_nlist);
        }

    }

    // ----------------------------------------------------------------------------------------

    #endif // DLIB_POSIX

    #endif // DLIB_GUI_CORE_KERNEL_2_CPp_