    // 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_1_CPp_
    #define DLIB_GUI_CORE_KERNEL_1_CPp_
    #include "../platform.h"

    #ifdef WIN32

    #include "gui_core_kernel_1.h"

    // tell visual studio to link to the libraries we need if we are
    // in fact using visual studio
    #ifdef _MSC_VER
    #pragma comment (lib, "gdi32.lib")
    #pragma comment (lib, "comctl32.lib")
    #pragma comment (lib, "user32.lib")
    #pragma comment (lib, "imm32.lib")
    #endif

    #include <cmath>
    #include <memory>
    #include <sstream>
    #include <vector>

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

    namespace dlib
    {

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

        namespace gui_core_kernel_1_globals
        {

            struct user_event_type
            {
                HWND w;
                void* p;
                int i;
            };

            typedef sync_extension<binary_search_tree<HWND,base_window*>::kernel_1a>::kernel_1a window_table_type;
            typedef sync_extension<queue<user_event_type,memory_manager<char>::kernel_1b>::kernel_2a_c>::kernel_1a queue_of_user_events;

            enum USER_OFFSETS
            {
                CREATE_WINDOW,
                DESTROY_WINDOW,
                SET_ACTIVE_WINDOW,
                QUIT_EVENT_HANDLER_THREAD,
                USER_EVENTS_READY,
                CALL_MOVE_WINDOW,
                SHOW_WINDOW_SHOW,
                SHOW_WINDOW_HIDE,
                CALL_SET_WINDOW_TITLE
            };

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

            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;

                queue_of_user_events user_events;
                queue_of_user_events user_events_temp;
                logger dlog;

                HINSTANCE hInstance;
                HWND helper_window;
                const TCHAR* window_class_name;

                bool quit_windows_loop;
                bool set_window_title_done;
                std::wstring window_title;
                bool move_window_done;
                HWND move_window_hwnd;
                int move_window_width;
                int move_window_height;
                int move_window_x;
                int move_window_y;
                bool request_new_window;
                DWORD dwStyle;
                HWND new_window;
                bool in_ime_composition;
                bool event_thread_started;
                // the window_table.get_mutex() mutex locks the above 11 variables

                // this variable holds a mapping from window handles to the base_window
                // objects which represent them.  Note that this objects mutex is always locked
                // when inside the event loop.
                window_table_type window_table;
                rsignaler window_close_signaler;
                rsignaler et_signaler;

                // note that this is the thread that will perform all the event
                // processing.
                thread_id_type event_thread_id;

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

                event_handler_thread(
                ) :
                    dlog("dlib.gui_core"),
                    hInstance(0),
                    helper_window(0),
                    window_class_name(TEXT ("w3049u6qc2d94thw9m34f4we0gvwa3-tgkser0-b9gm 05")),
                    quit_windows_loop(false),
                    set_window_title_done(true),
                    move_window_done(true),
                    move_window_hwnd(0),
                    move_window_width(0),
                    move_window_height(0),
                    move_window_x(0),
                    move_window_y(0),
                    request_new_window(false),
                    dwStyle(0),
                    new_window(0),
                    in_ime_composition(false),
                    event_thread_started(false),
                    window_close_signaler(window_table.get_mutex()),
                    et_signaler(window_table.get_mutex()),
                    reference_to_global_mutex(global_mutex())
                {
                    status = uninitialized;
                }

                void start_event_thread (
                )
                /*!
                    we can't call this function from this objects constructor because
                    starting the event thread in windows involves sending messages to the
                    WndProc() and that requires this object to be fully constructed.
                !*/
                {

                    if (event_thread_started == false)
                    {
                        auto_mutex M(window_table.get_mutex());
                        if (event_thread_started == false)
                        {
                            event_thread_started = true;
                            // 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 start event thread");
                        }
                    }
                }

                ~event_handler_thread ()
                {
                    using namespace gui_core_kernel_1_globals;

                    if (is_alive())
                    {
                        if (PostMessage(helper_window,WM_USER+QUIT_EVENT_HANDLER_THREAD,0,0)==0)
                        {
                            dlog << LWARN << "Unable to schedule function for execution in event handling thread.";
                            // No point calling wait() here since the thread isn't going to
                            // terminate gracefully in this case.  So we just let the program
                            // end as it will and hope for the best.
                        }
                        else
                        {
                            // wait for the event handler thread to terminate.
                            wait();
                        }
                    }

                }

            private:

                void thread (
                )
                {
                    event_thread_id = get_thread_id();

                    hInstance = GetModuleHandle(NULL);
                    if (hInstance == NULL)
                    {
                        dlog << LFATAL << "Error gathering needed resources";

                        // signal that an error has occurred
                        window_table.get_mutex().lock();
                        status = failure_to_init;
                        et_signaler.broadcast();
                        window_table.get_mutex().unlock();
                        return;
                    }

                    // register the main window class
                    WNDCLASS     wndclass ;

                    wndclass.style         = CS_DBLCLKS;
                    wndclass.lpfnWndProc   = dlib::gui_core_kernel_1_globals::WndProc ;
                    wndclass.cbClsExtra    = 0 ;
                    wndclass.cbWndExtra    = 0 ;
                    wndclass.hInstance     = hInstance ;
                    wndclass.hIcon         = LoadIcon (NULL, IDI_APPLICATION) ;
                    wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
                    wndclass.hbrBackground = 0;
                    wndclass.lpszMenuName  = NULL ;
                    wndclass.lpszClassName = window_class_name ;

                    if (!RegisterClass (&wndclass))
                    {
                        dlog << LFATAL << "Error registering window class";

                        // signal that an error has occurred
                        window_table.get_mutex().lock();
                        status = failure_to_init;
                        et_signaler.broadcast();
                        window_table.get_mutex().unlock();
                        return;
                    }

                    // make the helper window that is used to trigger events in the
                    // event handler loop from other threads
                    TCHAR nothing[] = TEXT("");
                    helper_window = CreateWindow(window_class_name,nothing,WS_DISABLED,0,0,0,0,HWND_MESSAGE,NULL,hInstance,NULL);
                    if (helper_window == NULL)
                    {
                        dlog << LFATAL << "Error gathering needed resources";

                        // signal that an error has occurred
                        window_table.get_mutex().lock();
                        status = failure_to_init;
                        et_signaler.broadcast();
                        window_table.get_mutex().unlock();
                        return;
                    }

                    // 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 loop.
                    /*
                        A note about this quit_windows_loop thing.  If the user is holding
                        the mouse button down on the title bar of a window it will cause
                        the PostQuitMessage() function to be ignored!!  This extra bool
                        is a work around to prevent that from happening.
                    */
                    MSG msg;
                    while (GetMessage (&msg, NULL, 0, 0) &&
                           quit_windows_loop == false)
                    {
                        TranslateMessage (&msg) ;
                        DispatchMessage (&msg) ;
                    }
                }
            };

            // 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;

            // Note that we need to use dlib::shared_ptr_thread_safe here rather than
            // std::shared_ptr.  This is because the destructor of event_handler_thread
            // triggers an event in the main event loop telling it to stop and that event loop
            // holds a shared pointer to the event_handler_thread.  So what can happen is
            // global_data()'s shared pointer gets destructed because the program is
            // terminating, which causes the event_handler_thread destructor to run, which
            // eventually causes the event loop to ask global_data() for a handle to the event
            // thread.  This is bad for std::shared_ptr since (at least as of 2017) most
            // implementations of std::shared_ptr decrement the reference count to 0 before
            // invoking the event_handler_thread's destructor and then when the event thread
            // calls global_data() it increments the counter again, then decrements it back to
            // 0, triggering a double deletion, when the event handler routine finally
            // finishes.
            //
            // dlib::shared_ptr_thread_safe doesn't have this problem.  An alternative would be
            // to somehow avoid this kind of self reference.  But it's not obvious how to do
            // that given the limitations of the Win32 event WndProc() structure imposed by
            // windows.  So in any case, we just use the old dlib::shared_ptr_thread_safe to
            // avoid this problem.
            const shared_ptr_thread_safe<event_handler_thread>& global_data()
            {
                auto_mutex M(*global_mutex());
                static shared_ptr_thread_safe<event_handler_thread> p;
                if (p.get() == 0)
                {
                    p.reset(new event_handler_thread());
                    M.unlock();
                    p->start_event_thread();
                }
                return p;
            }

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

            struct ebh_param
            {
                std::string text;
                std::string title;
            };

            static void error_box_helper(void* param)
            {
                ebh_param& p = *static_cast<ebh_param*>(param);
    #ifdef UNICODE
                MessageBox (NULL,  convert_mbstring_to_wstring(p.text).c_str(),
                            convert_mbstring_to_wstring(p.title).c_str(), MB_OK|MB_ICONERROR|MB_SYSTEMMODAL
                            );
    #else
                MessageBox (NULL,  p.text.c_str(),
                            p.title.c_str(), MB_OK|MB_ICONERROR|MB_SYSTEMMODAL
                            );
    #endif
                delete &p;
            }

            static void error_box (
                const char* title,
                const char* text,
                bool nonblocking = false
            )
            {
                try
                {
                    if (nonblocking)
                    {
                        ebh_param* param = new ebh_param;
                        param->text = text;
                        param->title = title;
                        dlib::create_new_thread(error_box_helper,param);
                    }
                    else
                    {
    #ifdef UNICODE
                        MessageBox (NULL, convert_mbstring_to_wstring(text).c_str(),
                                    convert_mbstring_to_wstring(title).c_str(),
                                    MB_OK|MB_ICONERROR|MB_SYSTEMMODAL
                                    );
    #else
                        MessageBox (NULL,  text,
                                    title, MB_OK|MB_ICONERROR|MB_SYSTEMMODAL
                                    );
    #endif
                    }
                }
                catch (...)
                {
                    // we are totally screwed if this happens so just quit
                    exit(0);
                }
            }

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

            static bool map_keys (
                unsigned long keycode,
                bool shift,
                bool caps,
                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 <= '9' && keycode >= '0')
                {
                    result = keycode;
                    if (shift)
                    {
                        switch (result)
                        {
                        case '0': result = ')'; break;
                        case '1': result = '!'; break;
                        case '2': result = '@'; break;
                        case '3': result = '#'; break;
                        case '4': result = '$'; break;
                        case '5': result = '%'; break;
                        case '6': result = '^'; break;
                        case '7': result = '&'; break;
                        case '8': result = '*'; break;
                        case '9': result = '('; break;
                        }
                    }
                }
                else if (keycode <= 'Z' && keycode >= 'A')
                {
                    result = keycode;

                    // make the result lower case if we need to.
                    if ((shift && caps) || (!caps && !shift))
                        result = result - 'A' + 'a';
                }
                else
                {
                    switch (keycode)
                    {
                    case VK_BACK:
                        is_printable = false;
                        result = base_window::KEY_BACKSPACE;
                        break;

                    case VK_SHIFT:
                        is_printable = false;
                        result = base_window::KEY_SHIFT;
                        break;

                    case VK_CONTROL:
                        is_printable = false;
                        result = base_window::KEY_CTRL;
                        break;

                    case VK_MENU:
                        is_printable = false;
                        result = base_window::KEY_ALT;
                        break;

                    case VK_PAUSE:
                        is_printable = false;
                        result = base_window::KEY_PAUSE;
                        break;

                    case VK_CAPITAL:
                        is_printable = false;
                        result = base_window::KEY_CAPS_LOCK;
                        break;

                    case VK_ESCAPE:
                        is_printable = false;
                        result = base_window::KEY_ESC;
                        break;

                    case VK_PRIOR:
                        is_printable = false;
                        result = base_window::KEY_PAGE_UP;
                        break;

                    case VK_NEXT:
                        is_printable = false;
                        result = base_window::KEY_PAGE_DOWN;
                        break;

                    case VK_END:
                        is_printable = false;
                        result = base_window::KEY_END;
                        break;

                    case VK_HOME:
                        is_printable = false;
                        result = base_window::KEY_HOME;
                        break;

                    case VK_LEFT:
                        is_printable = false;
                        result = base_window::KEY_LEFT;
                        break;

                    case VK_RIGHT:
                        is_printable = false;
                        result = base_window::KEY_RIGHT;
                        break;

                    case VK_UP:
                        is_printable = false;
                        result = base_window::KEY_UP;
                        break;

                    case VK_DOWN:
                        is_printable = false;
                        result = base_window::KEY_DOWN;
                        break;

                    case VK_INSERT:
                        is_printable = false;
                        result = base_window::KEY_INSERT;
                        break;

                    case VK_DELETE:
                        is_printable = false;
                        result = base_window::KEY_DELETE;
                        break;

                    case 0x91:
                        is_printable = false;
                        result = base_window::KEY_SCROLL_LOCK;
                        break;

                    case VK_F1:
                        is_printable = false;
                        result = base_window::KEY_F1;
                        break;

                    case VK_F2:
                        is_printable = false;
                        result = base_window::KEY_F2;
                        break;

                    case VK_F3:
                        is_printable = false;
                        result = base_window::KEY_F3;
                        break;

                    case VK_F4:
                        is_printable = false;
                        result = base_window::KEY_F4;
                        break;

                    case VK_F5:
                        is_printable = false;
                        result = base_window::KEY_F5;
                        break;

                    case VK_F6:
                        is_printable = false;
                        result = base_window::KEY_F6;
                        break;

                    case VK_F7:
                        is_printable = false;
                        result = base_window::KEY_F7;
                        break;

                    case VK_F8:
                        is_printable = false;
                        result = base_window::KEY_F8;
                        break;

                    case VK_F9:
                        is_printable = false;
                        result = base_window::KEY_F9;
                        break;

                    case VK_F10:
                        is_printable = false;
                        result = base_window::KEY_F10;
                        break;

                    case VK_F11:
                        is_printable = false;
                        result = base_window::KEY_F11;
                        break;

                    case VK_F12:
                        is_printable = false;
                        result = base_window::KEY_F12;
                        break;

                    case VK_SPACE:  result = ' ';  break;
                    case VK_TAB:    result = '\t'; break;
                    case VK_RETURN: result = '\n'; break;
                    case VK_NUMPAD0:  result = '0';  break;
                    case VK_NUMPAD1:  result = '1';  break;
                    case VK_NUMPAD2:  result = '2';  break;
                    case VK_NUMPAD3:  result = '3';  break;
                    case VK_NUMPAD4:  result = '4';  break;
                    case VK_NUMPAD5:  result = '5';  break;
                    case VK_NUMPAD6:  result = '6';  break;
                    case VK_NUMPAD7:  result = '7';  break;
                    case VK_NUMPAD8:  result = '8';  break;
                    case VK_NUMPAD9:  result = '9';  break;

                    case VK_MULTIPLY:   result = '*';  break;
                    case VK_ADD:        result = '+';  break;
                    case VK_SUBTRACT:   result = '-';  break;
                    case VK_DECIMAL:    result = '.';  break;
                    case VK_DIVIDE:     result = '/';  break;

                    case VK_OEM_1:
                        if (shift)  result = ':';
                        else        result = ';';
                        break;

                    case VK_OEM_PLUS:
                        if (shift)  result = '+';
                        else        result = '=';
                        break;

                    case VK_OEM_COMMA:
                        if (shift)  result = '<';
                        else        result = ',';
                        break;

                    case VK_OEM_MINUS:
                        if (shift)  result = '_';
                        else        result = '-';
                        break;

                    case VK_OEM_PERIOD:
                        if (shift)  result = '>';
                        else        result = '.';
                        break;

                    case VK_OEM_2:
                        if (shift)  result = '?';
                        else        result = '/';
                        break;

                    case VK_OEM_3:
                        if (shift)  result = '~';
                        else        result = '`';
                        break;

                    case VK_OEM_4:
                        if (shift)  result = '{';
                        else        result = '[';
                        break;

                    case VK_OEM_5:
                        if (shift)  result = '|';
                        else        result = '\\';
                        break;

                    case VK_OEM_6:
                        if (shift)  result = '}';
                        else        result = ']';
                        break;

                    case VK_OEM_7:
                        if (shift)  result = '"';
                        else        result = '\'';
                        break;

                    default:
                        return false;
                    }
                }

                return true;
            }

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

            LRESULT CALLBACK WndProc (
                HWND hwnd,
                UINT message,
                WPARAM wParam,
                LPARAM lParam
            )
            {
                using namespace gui_core_kernel_1_globals;
                // Make the event processing thread have a priority slightly above normal.
                // This makes the GUI smother if you do heavy processing in other threads.
                HANDLE hand = OpenThread(THREAD_ALL_ACCESS,FALSE,GetCurrentThreadId());
                SetThreadPriority(hand,THREAD_PRIORITY_ABOVE_NORMAL);
                CloseHandle(hand);

                auto globals = global_data();

                window_table_type& window_table = globals->window_table;
                HWND& helper_window = globals->helper_window;

                auto_mutex M(window_table.get_mutex());

                try
                {
                    std::vector<unsigned char> bitmap_buffer;

                    bool is_double = false;
                    unsigned long btn = base_window::NONE;

                    switch (message)
                    {
                        case WM_USER+QUIT_EVENT_HANDLER_THREAD:
                            if (hwnd == helper_window)
                            {
                                globals->quit_windows_loop = true;
                                PostQuitMessage(0);
                            }
                            return 0;

                        case WM_USER+DESTROY_WINDOW:
                            if (hwnd == helper_window)
                            {
                                DestroyWindow((HWND)wParam);
                            }
                            return 0;

                        case WM_USER+CALL_MOVE_WINDOW:
                            if (hwnd == helper_window)
                            {
                                MoveWindow(
                                    globals->move_window_hwnd,
                                    globals->move_window_x,
                                    globals->move_window_y,
                                    globals->move_window_width,
                                    globals->move_window_height,
                                    TRUE);
                                globals->move_window_done = true;
                                globals->et_signaler.broadcast();
                            }
                            return 0;

                        case WM_USER+USER_EVENTS_READY:
                            if (hwnd == helper_window)
                            {
                                // this is the signal to look in the user_events queue
                                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_ = 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();
                            }
                            return 0;

                        case WM_USER+SET_ACTIVE_WINDOW:
                            if (hwnd == helper_window)
                            {
                                SetActiveWindow((HWND)wParam);
                            }
                            return 0;

                        case WM_USER+SHOW_WINDOW_SHOW:
                            if (hwnd == helper_window)
                            {
                                ShowWindow((HWND)wParam,SW_SHOW);
                                BringWindowToTop((HWND)wParam);
                            }
                            return 0;

                        case WM_USER+SHOW_WINDOW_HIDE:
                            if (hwnd == helper_window)
                            {
                                ShowWindow((HWND)wParam,SW_HIDE);
                            }
                            return 0;

                        case WM_USER+CALL_SET_WINDOW_TITLE:
                            if (hwnd == helper_window)
                            {
                                SetWindowTextW((HWND)wParam,globals->window_title.c_str());
                                globals->set_window_title_done = true;
                                globals->et_signaler.broadcast();
                            }
                            return 0;

                        case WM_USER+CREATE_WINDOW:
                            if (hwnd == helper_window)
                            {

                                // if this is stupposed to be a popup window then do the popup window thing
                                if (globals->dwStyle == WS_CHILD)
                                {
                                    TCHAR nothing[] = TEXT("");
                                    globals->new_window = CreateWindowEx (WS_EX_TOOLWINDOW,globals->window_class_name, nothing,
                                        globals->dwStyle,
                                        CW_USEDEFAULT, CW_USEDEFAULT,
                                        CW_USEDEFAULT, CW_USEDEFAULT,
                                        helper_window, NULL, globals->hInstance, NULL);
                                    SetParent(globals->new_window,NULL);
                                }
                                else
                                {
                                    TCHAR nothing[] = TEXT("");
                                    globals->new_window = CreateWindow (globals->window_class_name, nothing,
                                        globals->dwStyle,
                                        CW_USEDEFAULT, CW_USEDEFAULT,
                                        CW_USEDEFAULT, CW_USEDEFAULT,
                                        NULL, NULL, globals->hInstance, NULL);
                                }
                                // use the helper_window to indicate that CreateWindow failed
                                if (globals->new_window == NULL)
                                    globals->new_window = helper_window;
                                globals->et_signaler.broadcast();
                            }
                            return 0;

                        case WM_SYSKEYDOWN:
                        case WM_KEYDOWN:
                            {
                                if (globals->in_ime_composition) break;

                                base_window** win_ = window_table[hwnd];
                                base_window* win;
                                if (win_)
                                    win = *win_;
                                else
                                    break;

                                unsigned long state = 0;

                                bool shift = ((GetKeyState(VK_SHIFT)&0x8000)!=0);
                                bool ctrl = ((GetKeyState(VK_CONTROL)&0x8000)!=0);
                                bool caps = ((GetKeyState(VK_CAPITAL)&0x0001)!=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((GetKeyState(VK_MENU)&0x8000)!=0)
                                    state |= base_window::KBD_MOD_ALT;
                                if((GetKeyState(VK_NUMLOCK)&0x0001)!=0)
                                    state |= base_window::KBD_MOD_NUM_LOCK;
                                if((GetKeyState(VK_SCROLL)&0x0001)!=0)
                                    state |= base_window::KBD_MOD_SCROLL_LOCK;

                                bool is_printable;
                                unsigned long result;

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

                            }
                            break;

                            // treat the user releasing the mouse button on the non client area (e.g. the title bar)
                            // like focus being lost since that is what X11 does
                        case WM_NCLBUTTONUP:
                        case WM_NCMBUTTONUP:
                        case WM_NCRBUTTONUP:
                        case WM_SETFOCUS:
                            {
                                base_window** win_ = window_table[hwnd];
                                base_window* win;
                                if (win_)
                                    win = *win_;
                                else
                                    break;

                                // signal that the window is gaining focus
                                win->on_focus_gained();
                            }
                            break;

                            // treat the user clicking on the non client area (e.g. the title bar)
                            // like focus being lost since that is what X11 does
                        case WM_NCLBUTTONDBLCLK:
                        case WM_NCMBUTTONDBLCLK:
                        case WM_NCRBUTTONDBLCLK:
                        case WM_NCLBUTTONDOWN:
                        case WM_NCMBUTTONDOWN:
                        case WM_NCRBUTTONDOWN:
                        case WM_KILLFOCUS:
                            {
                                base_window** win_ = window_table[hwnd];
                                base_window* win;
                                if (win_)
                                    win = *win_;
                                else
                                    break;

                                // signal that the window is gaining focus
                                win->on_focus_lost();
                            }
                            break;

                        case WM_SIZE:
                            {
                                base_window** win_ = window_table[hwnd];
                                base_window* win;
                                if (win_)
                                    win = *win_;
                                else
                                    break;

                                // signal that the window has been resized
                                win->on_window_resized();

                            }
                            return 0;

                        case WM_MOVE:
                            {
                                base_window** win_ = window_table[hwnd];
                                base_window* win;
                                if (win_)
                                    win = *win_;
                                else
                                    break;

                                // signal that the window has moved
                                win->on_window_moved();

                            }
                            return 0;

                        case WM_MOUSELEAVE:
                            {
                                base_window** win_ = window_table[hwnd];
                                base_window* win;
                                if (win_)
                                    win = *win_;
                                else
                                    break;

                                // signal that the mouse has left the window
                                if (win->mouse_in)
                                {
                                    win->on_mouse_leave();
                                    win->mouse_in = false;
                                }

                            }
                            return 0;

                        case WM_MOUSEWHEEL:
                            {
                                base_window** win_ = window_table[hwnd];
                                base_window* win;
                                if (win_)
                                    win = *win_;
                                else
                                    break;

                                unsigned long state = 0;
                                if (wParam & MK_CONTROL)
                                    state |= base_window::CONTROL;
                                if (wParam & MK_LBUTTON)
                                    state |= base_window::LEFT;
                                if (wParam & MK_MBUTTON)
                                    state |= base_window::MIDDLE;
                                if (wParam & MK_RBUTTON)
                                    state |= base_window::RIGHT;
                                if (wParam & MK_SHIFT)
                                    state |= base_window::SHIFT;

                                // signal the mouse wheel event
                                if (GET_WHEEL_DELTA_WPARAM(wParam) > 0)
                                {
                                    win->on_wheel_up(state);
                                }
                                else
                                {
                                    win->on_wheel_down(state);
                                }

                            }
                            return 0;

                        case WM_LBUTTONUP:
                            btn = base_window::LEFT;
                        case WM_MBUTTONUP:
                            if (btn == base_window::NONE)
                                btn = base_window::MIDDLE;
                        case WM_RBUTTONUP:
                            if (btn == base_window::NONE)
                                btn = base_window::RIGHT;
                            {
                                // release the mouse capture if the user isn't holding any
                                // other mouse buttons
                                if (!((wParam & MK_LBUTTON) | (wParam & MK_MBUTTON) | (wParam & MK_RBUTTON)))
                                    ReleaseCapture();

                                base_window** win_ = window_table[hwnd];
                                base_window* win;
                                if (win_)
                                    win = *win_;
                                else
                                    break;

                                unsigned long state = 0;
                                if (wParam & MK_CONTROL)
                                    state |= base_window::CONTROL;
                                if (wParam & MK_LBUTTON)
                                    state |= base_window::LEFT;
                                if (wParam & MK_MBUTTON)
                                    state |= base_window::MIDDLE;
                                if (wParam & MK_RBUTTON)
                                    state |= base_window::RIGHT;
                                if (wParam & MK_SHIFT)
                                    state |= base_window::SHIFT;

                                // remove the clicked button from the state
                                state &= (~btn);

                                // signal the mouse click
                                win->on_mouse_up(btn,state,GET_X_LPARAM(lParam),GET_Y_LPARAM(lParam));

                            }
                            return 0;

                        case WM_LBUTTONDBLCLK:
                            if (btn == base_window::NONE)
                                btn = base_window::LEFT;
                        case WM_MBUTTONDBLCLK:
                            if (btn == base_window::NONE)
                                btn = base_window::MIDDLE;
                        case WM_RBUTTONDBLCLK:
                            if (btn == base_window::NONE)
                                btn = base_window::RIGHT;
                            is_double = true;
                        case WM_LBUTTONDOWN:
                            if (btn == base_window::NONE)
                                btn = base_window::LEFT;
                        case WM_MBUTTONDOWN:
                            if (btn == base_window::NONE)
                                btn = base_window::MIDDLE;
                        case WM_RBUTTONDOWN:
                            if (btn == base_window::NONE)
                                btn = base_window::RIGHT;
                            {
                                SetCapture(hwnd);

                                base_window** win_ = window_table[hwnd];
                                base_window* win;
                                if (win_)
                                    win = *win_;
                                else
                                    break;

                                unsigned long state = 0;
                                if (wParam & MK_CONTROL)
                                    state |= base_window::CONTROL;
                                if (wParam & MK_LBUTTON)
                                    state |= base_window::LEFT;
                                if (wParam & MK_MBUTTON)
                                    state |= base_window::MIDDLE;
                                if (wParam & MK_RBUTTON)
                                    state |= base_window::RIGHT;
                                if (wParam & MK_SHIFT)
                                    state |= base_window::SHIFT;

                                // remove the clicked button from the state
                                state &= (~btn);

                                // signal the mouse click
                                win->on_mouse_down(btn,state,GET_X_LPARAM(lParam),GET_Y_LPARAM(lParam),is_double);

                            }
                            return 0;

                        case WM_MOUSEMOVE:
                            {
                                base_window** win_ = window_table[hwnd];
                                base_window* win;
                                if (win_)
                                    win = *win_;
                                else
                                    break;

                                unsigned long state = 0;
                                bool mouse_button_down = false;
                                if (wParam & MK_CONTROL)
                                    state |= base_window::CONTROL;
                                if (wParam & MK_LBUTTON)
                                {
                                    state |= base_window::LEFT;
                                    mouse_button_down = true;
                                }
                                if (wParam & MK_MBUTTON)
                                {
                                    mouse_button_down = true;
                                    state |= base_window::MIDDLE;
                                }
                                if (wParam & MK_RBUTTON)
                                {
                                    state |= base_window::RIGHT;
                                    mouse_button_down = true;
                                }
                                if (wParam & MK_SHIFT)
                                    state |= base_window::SHIFT;

                                // signal the mouse movement if this mouse event isn't identical to the
                                // last one we got
                                if ( GET_X_LPARAM(lParam) != win->prevx ||
                                     GET_Y_LPARAM(lParam) != win->prevy ||
                                     state != win->prev_state)
                                {
                                    win->on_mouse_move(state,GET_X_LPARAM(lParam),GET_Y_LPARAM(lParam));
                                }

                                // save the event data into the prev* member variables
                                win->prevx = GET_X_LPARAM(lParam);
                                win->prevy = GET_Y_LPARAM(lParam);
                                win->prev_state = state;

                                // The following block of code checks if the mouse is moving
                                // into or out of the window.
                                if (mouse_button_down == false)
                                {
                                    // if there isn't any mouse button down then the fact that
                                    // we are getting a mouse move message means it is in the
                                    // window
                                    if (win->mouse_in == false)
                                    {
                                        win->on_mouse_enter();
                                        win->mouse_in = true;

                                        // set the tracker for the mouse
                                        TRACKMOUSEEVENT tm;
                                        tm.hwndTrack = hwnd;
                                        tm.cbSize = sizeof(tm);
                                        tm.dwFlags = TME_LEAVE;
                                        _TrackMouseEvent(&tm);
                                    }
                                }
                                else if (win->mouse_in)
                                {
                                    // check if the mouse is currently outside the window
                                    const long mouse_x = GET_X_LPARAM(lParam);
                                    const long mouse_y = GET_Y_LPARAM(lParam);
                                    if (mouse_x < 0 || mouse_y < 0)
                                    {
                                        // the mouse is not in the window
                                        win->mouse_in = false;
                                        win->on_mouse_leave();
                                    }
                                    else
                                    {
                                        unsigned long width, height;
                                        win->get_size(width,height);
                                        if (mouse_x >= static_cast<long>(width) ||
                                            mouse_y >= static_cast<long>(height))
                                        {
                                            // the mouse is not in the window
                                            win->mouse_in = false;
                                            win->on_mouse_leave();
                                        }
                                    }
                                }
                                else if (win->mouse_in == false)
                                {
                                    // at this point we know that the mouse is moving around
                                    // with some of its buttons down.  So it might be outside the window.
                                    // get the window size and see if the mouse is outside
                                    // it.
                                    const long mouse_x = GET_X_LPARAM(lParam);
                                    const long mouse_y = GET_Y_LPARAM(lParam);
                                    unsigned long width, height;
                                    win->get_size(width,height);
                                    if (mouse_x < static_cast<long>(width) &&
                                        mouse_y < static_cast<long>(height) &&
                                        mouse_x >= 0 &&
                                        mouse_y >= 0)
                                    {
                                        // The mouse has gone inside the window
                                        win->mouse_in = true;
                                        win->on_mouse_enter();

                                        // set the tracker for the mouse
                                        TRACKMOUSEEVENT tm;
                                        tm.hwndTrack = hwnd;
                                        tm.cbSize = sizeof(tm);
                                        tm.dwFlags = TME_LEAVE;
                                        _TrackMouseEvent(&tm);
                                    }

                                }

                            }
                            return 0;

                        case WM_PAINT :
                            {

                                PAINTSTRUCT ps;
                                HDC   hdc = NULL;

                                hdc = BeginPaint (hwnd, &ps) ;

                                try
                                {
                                    base_window** win_ = window_table[hwnd];
                                    base_window* win;
                                    if (win_)
                                        win = *win_;
                                    else
                                        break;

                                    LONG x = ps.rcPaint.left;
                                    LONG y = ps.rcPaint.top;
                                    LONG width = ps.rcPaint.right - x;
                                    LONG height = ps.rcPaint.bottom - y;

                                    if (width != 0 && height != 0)
                                    {

                                        BITMAPINFO bmap_info;
                                        bmap_info.bmiColors[0].rgbBlue = 0;
                                        bmap_info.bmiColors[0].rgbGreen = 0;
                                        bmap_info.bmiColors[0].rgbRed = 0;
                                        bmap_info.bmiColors[0].rgbReserved = 0;
                                        bmap_info.bmiHeader.biSize = sizeof(bmap_info.bmiHeader);
                                        bmap_info.bmiHeader.biWidth = width;
                                        bmap_info.bmiHeader.biHeight = -1*height;
                                        bmap_info.bmiHeader.biPlanes = 1;
                                        bmap_info.bmiHeader.biBitCount = 24;
                                        bmap_info.bmiHeader.biCompression = BI_RGB;
                                        bmap_info.bmiHeader.biSizeImage = 0;
                                        bmap_info.bmiHeader.biXPelsPerMeter = 0;
                                        bmap_info.bmiHeader.biYPelsPerMeter = 0;
                                        bmap_info.bmiHeader.biClrUsed = 0;
                                        bmap_info.bmiHeader.biClrImportant = 0;

                                        unsigned char* bitmap ;
                                        unsigned long size;
                                        unsigned long padding = 0;
                                        if ((width*3)%sizeof(LONG) != 0)
                                        {
                                            padding = sizeof(LONG) - (width*3)%sizeof(LONG);
                                            size = (width*3+padding)*height;
                                        }
                                        else
                                        {
                                            size = width*height*3;
                                        }

                                        if (bitmap_buffer.size() < size)
                                            bitmap_buffer.resize(size);
                                        bitmap = &bitmap_buffer[0];

                                        canvas bits(bitmap,padding,x,y,x+width-1,y+height-1);

                                        win->paint(bits);

                                        SetDIBitsToDevice (
                                            hdc,
                                            ps.rcPaint.left,
                                            ps.rcPaint.top,
                                            width,
                                            height,
                                            0,
                                            0,
                                            0,
                                            height,
                                            bitmap,
                                            &bmap_info,
                                            DIB_RGB_COLORS
                                            );
                                    }

                                    EndPaint (hwnd, &ps) ;

                                }
                                catch (...)
                                {
                                    // make sure EndPaint is called even if an exception
                                    // is thrown.
                                    if (hdc != NULL)
                                        EndPaint (hwnd, &ps);
                                    throw;
                                }
                            }
                            return 0 ;

                        case  WM_ERASEBKGND:
                            return 1;

                        case WM_CLOSE:
                            {
                                base_window** win_ = window_table[hwnd];
                                base_window* win;
                                if (win_)
                                    win = *win_;
                                else
                                    break;

                                // signal that the window is being closed
                                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
                                        );
                                    // this happens if the on_window_close() callback
                                    // tells us to ignore the close event.
                                    return 0;
                                }
                                else
                                {
                                    if (window_table[hwnd])
                                    {
                                        window_table.destroy(hwnd);
                                        win->has_been_destroyed = true;
                                        win->hwnd = 0;
                                        globals->window_close_signaler.broadcast();
                                    }
                                    else
                                    {
                                        // in this case the window must have self destructed by
                                        // calling delete this;
                                        return 0;
                                    }
                                }

                            }
                            return DefWindowProc (hwnd, message, wParam, lParam);

                        case WM_IME_STARTCOMPOSITION:
                            globals->in_ime_composition = true;
                            break;

                        case WM_IME_COMPOSITION:
                            {
                            globals->in_ime_composition = false;
                            base_window** win_ = window_table[hwnd];
                            base_window* win;
                            if (win_)
                                win = *win_;
                            else
                                break;
                            HIMC hImc = ImmGetContext(hwnd);
                            if (lParam & GCS_RESULTSTR){
                                WCHAR wc;
                                LONG bufbyte = ImmGetCompositionStringW(hImc, GCS_RESULTSTR, &wc, 0);
                                if (bufbyte != IMM_ERROR_NODATA && bufbyte != IMM_ERROR_GENERAL){
                                    bufbyte += sizeof(WCHAR);

                                    WCHAR *buf = new WCHAR[bufbyte / sizeof(WCHAR)];
                                    ImmGetCompositionStringW(hImc, GCS_RESULTSTR, buf, bufbyte);
                                    buf[bufbyte / sizeof(WCHAR) - 1] = L'\0';

                                    // signal the putstring event
                                    win->on_string_put(std::wstring(buf));
                                    delete [] buf;
                                }
                            }
                            ImmReleaseContext(hwnd, hImc);
                            }
                            break;

                        default:
                            break;

                    } // switch (message)

                }
                catch (std::exception& e)
                {
                    error_box("Exception thrown in event handler",e.what());
                    globals->quit_windows_loop = true;
                }
                catch (...)
                {
                    error_box("Exception thrown in event handler","Unknown Exception type.");
                    globals->quit_windows_loop = true;
                }

                return DefWindowProc (hwnd, message, wParam, lParam) ;

            }

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

            void show_window (
                HWND hwnd
            )
            {
                using namespace gui_core_kernel_1_globals;
                PostMessage(global_data()->helper_window,WM_USER+SHOW_WINDOW_SHOW,(WPARAM)hwnd,0);
            }

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

            void hide_window (
                HWND hwnd
            )
            {
                using namespace gui_core_kernel_1_globals;
                PostMessage(global_data()->helper_window,WM_USER+SHOW_WINDOW_HIDE,(WPARAM)hwnd,0);
            }

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

            void give_window_focus (
                HWND hwnd
            )
            /*!
                ensures
                    - calls SetActiveWindow(hwnd) from the event handling thread.
            !*/
            {
                using namespace gui_core_kernel_1_globals;
                PostMessage(global_data()->helper_window,WM_USER+SET_ACTIVE_WINDOW,(WPARAM)hwnd,0);
            }

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

            void destroy_window (
                HWND hwnd
            )
            /*!
                ensures
                    - calls DestroyWindow(hwnd) from the event handling thread.
            !*/
            {
                using namespace gui_core_kernel_1_globals;
                PostMessage(global_data()->helper_window,WM_USER+DESTROY_WINDOW,(WPARAM)hwnd,0);
            }

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

            HWND make_window (
                DWORD dwStyle_
            )
            /*!
                ensures
                    - creates a window by calling CreateWindow and passes on the
                      dwStyle argument.
                    - returns the HWND that is returned by CreateWindow
                    - ensures that CreateWindow is called from the event handler thread
                    - if (it was unable to create a window) then
                        - returns NULL or helper_window
            !*/
            {
                using namespace gui_core_kernel_1_globals;
                auto globals = global_data();
                // if we are running in the event handling thread then just call
                // CreateWindow directly
                if (get_thread_id() == globals->event_thread_id)
                {
                    // if this is stupposed to be a popup window then do the popup window thing
                    if (dwStyle_ == WS_CHILD)
                    {
                        TCHAR nothing[] = TEXT("");
                        HWND tmp = CreateWindowEx (WS_EX_TOOLWINDOW|WS_EX_TOPMOST, globals->window_class_name, nothing,
                                dwStyle_,
                                CW_USEDEFAULT, CW_USEDEFAULT,
                                CW_USEDEFAULT, CW_USEDEFAULT,
                                globals->helper_window, NULL, globals->hInstance, NULL);
                        SetParent(tmp,NULL);
                        return tmp;
                    }
                    else
                    {
                        TCHAR nothing[] = TEXT("");
                        return CreateWindow (globals->window_class_name, nothing,
                                dwStyle_,
                                CW_USEDEFAULT, CW_USEDEFAULT,
                                CW_USEDEFAULT, CW_USEDEFAULT,
                                NULL, NULL, globals->hInstance, NULL);
                    }
                }
                else
                {
                    auto_mutex M(globals->window_table.get_mutex());
                    // wait for our chance to make a new window request
                    while (globals->request_new_window)
                        globals->et_signaler.wait();

                    globals->dwStyle = dwStyle_;
                    if (PostMessage(globals->helper_window,WM_USER+CREATE_WINDOW,0,0)==0)
                    {
                        throw gui_error("Unable to schedule function for execution in event handling thread.");
                    }

                    // wait for our request to be serviced
                    while (globals->new_window == NULL)
                        globals->et_signaler.wait();

                    HWND temp = globals->new_window;
                    globals->new_window = NULL;
                    globals->request_new_window = false;
                    globals->et_signaler.broadcast();

                    // if make_window() returns the helper_window then it means it failed
                    // to make a new window
                    if (temp == globals->helper_window)
                        temp = NULL;

                    return temp;
                }
            }

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

        } // end namespace gui_core_kernel_1_globals

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

        void canvas::
        fill (
            unsigned char red_,
            unsigned char green_,
            unsigned char blue_
        ) const
        {
            const unsigned long red = red_;
            const unsigned long green = green_;
            const unsigned long blue = blue_;

            const LONG block1 = (blue<<24) | (red<<16) | (green<<8) | blue;
            const LONG block2 = (green<<24) | (blue<<16) | (red<<8) | green;
            const LONG block3 = (red<<24) | (green<<16) | (blue<<8) | red;

            // remember that row_width is a multiple of 4 because windows
            // requires that all bitmaps have row widths that are multiples of 4.
            unsigned long size = row_width/4;
            for (unsigned long i = 0; i < height_; ++i)
            {
                unsigned long padding = size%3;
                LONG* start = reinterpret_cast<LONG*>(bits+row_width*i);
                LONG* end = reinterpret_cast<LONG*>(start) + size - padding;
                while (start != end)
                {
                    *start = block1;
                    ++start;
                    *start = block2;
                    ++start;
                    *start = block3;
                    ++start;
                }
                if (padding)
                {
                    *start = block1;
                    ++start;
                    --padding;
                }
                if (padding)
                {
                    *start = block2;
                }
            }
        }

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

        void base_window::
        trigger_user_event (
            void* p,
            int i
        )
        {
            using namespace gui_core_kernel_1_globals;

            user_event_type e;
            e.w = hwnd;
            e.p = p;
            e.i = i;
            {
                auto_mutex M(globals->user_events.get_mutex());
                globals->user_events.enqueue(e);
            }

            if (PostMessage(globals->helper_window,WM_USER+USER_EVENTS_READY,0,0)==0)
            {
                throw gui_error("Unable to schedule function for execution in event handling thread.");
            }
        }

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

        base_window::
        base_window (
            bool resizable,
            bool undecorated
        ) :
            globals(gui_core_kernel_1_globals::global_data()),
            has_been_destroyed(false),
            prevx(-1),
            prevy(-1),
            prev_state(0),
            wm(globals->window_table.get_mutex())
        {
            using namespace gui_core_kernel_1_globals;
            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
                );

            if (resizable)
                style = WS_OVERLAPPEDWINDOW;
            else if (undecorated)
                style = WS_CHILD;
            else
                style = WS_OVERLAPPEDWINDOW ^ WS_THICKFRAME ^ WS_MAXIMIZEBOX;

            hwnd = gui_core_kernel_1_globals::make_window(style);

            if (hwnd == NULL)
                throw gui_error("unable to create base_window");

            auto_mutex M(wm);

            mouse_in = false;

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

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

        base_window::
        ~base_window (
        )
        {
            using namespace gui_core_kernel_1_globals;
            close_window();
        }

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

        void base_window::
        close_window (
        )
        {
            using namespace gui_core_kernel_1_globals;
            auto_mutex M(wm);
            if (has_been_destroyed == false)
            {
                // do this just to make sure no one tries to call this window's
                // calbacks.
                globals->window_table.destroy(hwnd);
                gui_core_kernel_1_globals::destroy_window(hwnd);
                hwnd = 0;
                has_been_destroyed = true;
                globals->window_close_signaler.broadcast();
            }
        }

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

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

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

        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_1_globals;

            auto_mutex M(wm);
            if (has_been_destroyed == true)
                return;

            // call the SetWindowText function with our arguments but make sure it is from
            // the event thread.  We have to do this because the SetWindowText() apparently blocks
            // until something happens in the event thread so we have to
            // do this to avoid possible deadlocks.
            if (get_thread_id() == globals->event_thread_id)
            {
                SetWindowTextW(hwnd,title.c_str());
            }
            else
            {
                globals->window_title = title;
                globals->set_window_title_done = false;

                if (PostMessage(globals->helper_window,WM_USER+CALL_SET_WINDOW_TITLE,(WPARAM)hwnd,0)==0)
                {
                    throw gui_error("Unable to schedule SetWindowText function for execution in event handling thread.");
                }

                // wait for any SetWindowText() calls to finish
                while (globals->set_window_title_done == false)
                    globals->et_signaler.wait();
            }
        }

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

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

            show_window(hwnd);
            if (style != WS_CHILD)
                give_window_focus(hwnd);
        }

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

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

            hide_window(hwnd);
        }

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

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

            if (get_thread_id() == globals->event_thread_id)
            {
                RECT info;
                GetWindowRect(hwnd,&info);

                int x = info.left;
                int y = info.top;
                int width;
                int height;

                RECT rect;
                rect.top = 0;
                rect.left = 0;
                rect.bottom = height_;
                rect.right = width_;
                AdjustWindowRectEx(&rect,style,FALSE,0);

                width = std::abs(rect.right - rect.left);
                height = std::abs(rect.bottom - rect.top);

                MoveWindow(
                    hwnd,
                    x,
                    y,
                    width,
                    height,
                    TRUE);
            }
            else
            {
                RECT info;
                GetWindowRect(hwnd,&info);

                int x = info.left;
                int y = info.top;
                int width;
                int height;

                RECT rect;
                rect.top = 0;
                rect.left = 0;
                rect.bottom = height_;
                rect.right = width_;
                AdjustWindowRectEx(&rect,style,FALSE,0);

                width = std::abs(rect.right - rect.left);
                height = std::abs(rect.bottom - rect.top);

                // call the MoveWindow function with our arguments.  We
                // have to do this because the MoveWindow() apparently blocks
                // until something happens in the event thread so we have to
                // do this to avoid possible deadlocks.
                globals->move_window_hwnd = hwnd;
                globals->move_window_x = x;
                globals->move_window_y = y;
                globals->move_window_width = width;
                globals->move_window_height = height;
                globals->move_window_done = false;

                if (PostMessage(globals->helper_window,WM_USER+CALL_MOVE_WINDOW,0,0)==0)
                {
                    throw gui_error("Unable to schedule MoveWindow function for execution in event handling thread.");
                }

                // wait for any MoveWindow calls to finish
                while (globals->move_window_done == false)
                    globals->et_signaler.wait();
            }

        }

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

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

            if (get_thread_id() == globals->event_thread_id)
            {
                RECT info;
                GetWindowRect(hwnd,&info);
                int width = info.right - info.left;
                int height = info.bottom - info.top;

                MoveWindow(
                    hwnd,
                    x_,
                    y_,
                    width,
                    height,
                    TRUE);

            }
            else
            {
                RECT info;
                GetWindowRect(hwnd,&info);
                int width = info.right - info.left;
                int height = info.bottom - info.top;

                // call the MoveWindow function with our arguments.  We
                // have to do this because the MoveWindow() apparently blocks
                // until something happens in the event thread so we have to
                // do this to avoid possible deadlocks.
                globals->move_window_hwnd = hwnd;
                globals->move_window_x = x_;
                globals->move_window_y = y_;
                globals->move_window_width = width;
                globals->move_window_height = height;
                globals->move_window_done = false;

                if (PostMessage(globals->helper_window,WM_USER+CALL_MOVE_WINDOW,0,0)==0)
                {
                    throw gui_error("Unable to schedule MoveWindow function for execution in event handling thread.");
                }

                // wait for any MoveWindow calls to finish
                while (globals->move_window_done == false)
                    globals->et_signaler.wait();
            }
        }

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

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

            POINT p;
            p.x = 0;
            p.y = 0;
            ClientToScreen(hwnd,&p);

            x_ = p.x;
            y_ = p.y;
        }

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

        void base_window::
        get_display_size (
            unsigned long& width,
            unsigned long& height
        ) const
        {
            auto_mutex M(wm);
            width = 0;
            height = 0;
            if (has_been_destroyed == true)
                return;

            RECT rc;
            GetWindowRect(hwnd, &rc);

            HMONITOR hMonitor;
            MONITORINFO mi;
            //
            // get the nearest monitor to the passed rect.
            //
            hMonitor = MonitorFromRect(&rc, MONITOR_DEFAULTTONEAREST);

            //
            // get the work area or entire monitor rect.
            //
            mi.cbSize = sizeof(mi);
            GetMonitorInfo(hMonitor, &mi);

            rc = mi.rcMonitor;

            width = static_cast<unsigned long>(rc.right - rc.left);
            height = static_cast<unsigned long>(rc.bottom - rc.top);

        }

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

        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;

            RECT r;
            GetClientRect(hwnd,&r);

            width = r.right - r.left;
            height = r.bottom - r.top;
        }

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

        void base_window::
        invalidate_rectangle (
            const rectangle& rect
        )
        {
            auto_mutex M(wm);
            if (rect.is_empty() == false && !has_been_destroyed)
            {
                RECT info;
                info.top = rect.top();
                info.left = rect.left();
                info.right = rect.right()+1;
                info.bottom = rect.bottom()+1;

                InvalidateRect(hwnd,&info,FALSE);
            }
        }

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

        void base_window::
        set_im_pos (
            long x,
            long y
        )
        {
            auto_mutex M(wm);
            if (has_been_destroyed == true)
                return;

            HIMC hImc = ImmGetContext(hwnd);

            COMPOSITIONFORM cf;
            cf.dwStyle = CFS_POINT;
            cf.ptCurrentPos.x = x;
            cf.ptCurrentPos.y = y;
            ImmSetCompositionWindow(hImc, &cf);
            ImmReleaseContext(hwnd, hImc);
        }

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

        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_1_globals;
            using namespace std;

            auto globals = global_data();

            if (OpenClipboard(globals->helper_window))
            {
                EmptyClipboard();
                auto_mutex M(globals->window_table.get_mutex());

                const unsigned long newlines = count(str.begin(),str.end(),L'\n');

                HGLOBAL mem = GlobalAlloc(GMEM_MOVEABLE,(str.size()+newlines+1)*sizeof(wchar_t));
                if (mem != NULL)
                {
                    wchar_t* buf = reinterpret_cast<wchar_t*>(GlobalLock(mem));

                    if (buf != NULL)
                    {
                        // copy str into buf while also replacing all the \n with \r\n
                        for (wstring::size_type i = 0; i < str.size(); ++i)
                        {
                            if (str[i] != L'\n')
                            {
                                *buf = str[i];
                                ++buf;
                            }
                            else
                            {
                                *buf = L'\r';
                                ++buf;
                                *buf = L'\n';
                                ++buf;
                            }
                        }
                        *buf = L'\0';
                        GlobalUnlock(mem);
                        SetClipboardData(CF_UNICODETEXT,mem);
                    }
                }
                CloseClipboard();
            }
        }

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

        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_1_globals;
            using namespace std;
            auto globals = global_data();

            auto_mutex M(globals->window_table.get_mutex());
            if (OpenClipboard(globals->helper_window))
            {

                HANDLE data = GetClipboardData(CF_UNICODETEXT);
                if (data != NULL)
                {
                    wchar_t* buf = reinterpret_cast<wchar_t*>(GlobalLock(data));
                    if (buf != 0)
                    {
                        str.clear();

                        // copy the data from buf into str while also removing any '\r'
                        // characters.
                        while (*buf != L'\0')
                        {
                            if (*buf != L'\r')
                                str += *buf;
                            ++buf;
                        }

                        GlobalUnlock(data);
                    }
                    else
                    {
                        Beep(500,500);
                    }
                }

                CloseClipboard();
            }
        }

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

    }

    #endif // WIN32

    #endif // DLIB_GUI_CORE_KERNEL_1_CPp_