// Copyright (C) 2005 Davis E. King (davisking@users.sourceforge.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 <sstream>
#include "../threads.h"
#include "../assert.h"
#include "../queue.h"
#include "../sync_extension.h"
#include "../queue.h"
#include "../logger.h"
#include <cmath>
#include <vector>
namespace dlib
{
// ----------------------------------------------------------------------------------------
namespace gui_core_kernel_1_globals
{
static logger dlog("dlib.gui_core");
static TCHAR window_class_name[] = TEXT ("w3049u6qc2d94thw9m34f4we0gvwa3-tgkser0-b9gm 05");
static HINSTANCE hInstance;
static HWND helper_window = NULL;
static bool core_has_been_initialized = false;
static bool quit_windows_loop = false;
static bool set_window_title_done = true;
static std::wstring window_title;
static bool move_window_done = true;
static HWND move_window_hwnd = NULL;
static int move_window_width = 0;
static int move_window_height = 0;
static int move_window_x = 0;
static int move_window_y = 0;
static bool request_new_window = false;
static DWORD dwStyle;
static HWND new_window = NULL;
static bool in_ime_composition = false;
// the window_table.get_mutex() mutex locks the above 11 variables
typedef sync_extension<binary_search_tree<HWND,base_window*>::kernel_1a>::kernel_1a
window_table_type;
// 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.
// Also, put these objects on the heap because we want to ensure that they
// aren't destroyed until the event_handler is destroyed
static window_table_type& window_table = *(new window_table_type);
static rsignaler& window_close_signaler = *(new rsignaler(window_table.get_mutex()));
static rsignaler& et_signaler = *(new rsignaler(window_table.get_mutex()));
// note that this is the thread that will perform all the event
// processing.
thread_id_type event_thread_id;
struct user_event_type
{
HWND 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;
queue_of_user_events 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
};
// ----------------------------------------------------------------------------------------
struct ebh_param
{
std::string text;
std::string title;
};
static void error_box_helper(void* param)
{
ebh_param& p = *reinterpret_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;
queue_of_user_events user_events_temp;
// 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_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)
{
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(
move_window_hwnd,
move_window_x,
move_window_y,
move_window_width,
move_window_height,
TRUE);
move_window_done = true;
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
user_events.lock();
user_events.swap(user_events_temp);
user_events.unlock();
user_events_temp.reset();
// now dispatch all these user events
while (user_events_temp.move_next())
{
base_window** win_ = window_table[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(
user_events_temp.element().p,
user_events_temp.element().i
);
}
}
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,window_title.c_str());
set_window_title_done = true;
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 (dwStyle == WS_CHILD)
{
TCHAR nothing[] = TEXT("");
new_window = CreateWindowEx (WS_EX_TOOLWINDOW,window_class_name, nothing,
dwStyle,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
helper_window, NULL, hInstance, NULL);
SetParent(new_window,NULL);
}
else
{
TCHAR nothing[] = TEXT("");
new_window = CreateWindow (window_class_name, nothing,
dwStyle,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL, hInstance, NULL);
}
// use the helper_window to indicate that CreateWindow failed
if (new_window == NULL)
new_window = helper_window;
et_signaler.broadcast();
}
return 0;
case WM_SYSKEYDOWN:
case WM_KEYDOWN:
{
if (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;
// signal the mouse wheel event
if (GET_WHEEL_DELTA_WPARAM(wParam) > 0)
{
win->on_wheel_up();
}
else
{
win->on_wheel_down();
}
}
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;
gui_core_kernel_1_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:
in_ime_composition = true;
break;
case WM_IME_COMPOSITION:
{
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());
quit_windows_loop = true;
}
catch (...)
{
error_box("Exception thrown in event handler","Unknown Exception type.");
quit_windows_loop = true;
}
return DefWindowProc (hwnd, message, wParam, lParam) ;
}
// ----------------------------------------------------------------------------------------
void show_window (
HWND hwnd
)
{
using namespace gui_core_kernel_1_globals;
PostMessage(helper_window,WM_USER+SHOW_WINDOW_SHOW,(WPARAM)hwnd,0);
}
// ----------------------------------------------------------------------------------------
void hide_window (
HWND hwnd
)
{
using namespace gui_core_kernel_1_globals;
PostMessage(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(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(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;
// if we are running in the event handling thread then just call
// CreateWindow directly
if (get_thread_id() == gui_core_kernel_1_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, window_class_name, nothing,
dwStyle_,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
helper_window, NULL, hInstance, NULL);
SetParent(tmp,NULL);
return tmp;
}
else
{
TCHAR nothing[] = TEXT("");
return CreateWindow (window_class_name, nothing,
dwStyle_,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL, hInstance, NULL);
}
}
else
{
auto_mutex M(window_table.get_mutex());
// wait for our chance to make a new window request
while (request_new_window)
et_signaler.wait();
dwStyle = dwStyle_;
if (PostMessage(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 (new_window == NULL)
et_signaler.wait();
HWND temp = new_window;
new_window = NULL;
request_new_window = false;
et_signaler.broadcast();
// if make_window() returns the helper_window then it means it failed
// to make a new window
if (temp == helper_window)
temp = NULL;
return temp;
}
}
// ------------------------------------------------------------------------------------
class event_handler_thread : public threaded_object
{
public:
enum et_state
{
uninitialized,
initialized,
failure_to_init
};
et_state status;
event_handler_thread(
)
{
status = uninitialized;
register_program_ending_handler(*this, &event_handler_thread::self_destruct);
}
~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 << LERROR << "Unable to schedule function for execution in event handling thread.";
}
wait();
}
delete &et_signaler;
delete &window_close_signaler;
delete &window_table;
}
void self_destruct()
{
delete this;
}
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(