// 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_2_CPp_
#define DLIB_GUI_CORE_KERNEL_2_CPp_
#include "../platform.h"
#ifdef POSIX
#include "gui_core_kernel_2.h"
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/keysym.h>
#include <X11/Xlocale.h>
#include <poll.h>
#include <iostream>
#include "../assert.h"
#include "../queue.h"
#include <cstring>
#include <cmath>
#include <X11/Xatom.h>
#include "../sync_extension.h"
#include "../logger.h"
#include <vector>
#include <set>
namespace dlib
{
// ----------------------------------------------------------------------------------------
namespace gui_core_kernel_2_globals
{
static logger dlog("dlib.gui_core");
void event_handler ();
void trigger_user_event_threadproc (void*);
struct x11_base_windowstuff
{
Window hwnd;
Time last_click_time;
XIC xic;
};
typedef sync_extension<binary_search_tree<Window,base_window*>::kernel_1a>::kernel_1a
window_table_type;
int depth;
Display* disp;
static XIM xim;
static XIMStyle xim_style;
static Screen* screen;
Atom delete_window;
static Window exit_window;
static std::wstring clipboard;
static bool core_has_been_initialized = false;
static int alt_mask = 0;
static int meta_mask = 0;
static int num_lock_mask = 0;
static int scroll_lock_mask = 0;
// put these objects on the heap because we want to ensure that they
// aren't destroyed until the event_handler_thread_object 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()));
struct user_event_type
{
Window w;
void* p;
int i;
};
typedef sync_extension<queue<user_event_type,memory_manager<char>::kernel_1b>::kernel_2a_c>::kernel_1a queue_of_user_events;
queue_of_user_events user_events;
queue_of_user_events user_events_temp;
// ----------------------------------------------------------------------------------------
Bool XCheckIfEventPredicate (
Display* disp,
XEvent* event,
XPointer arg
)
/*!
ensures
- if (event is an Expose event for the window pointed to by arg) then
- returns true
- else
- returns false
!*/
{
if (event->type == Expose)
{
XExposeEvent* e = reinterpret_cast<XExposeEvent*>(event);
Window* win= reinterpret_cast<Window*>(arg);
if (e->window == *win)
{
return 1;
}
}
return 0;
}
// ----------------------------------------------------------------------------------------
static bool map_keys (
KeySym keycode,
bool 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 <= 'z' && keycode >= 'a' ||
keycode <= 'Z' && keycode >= 'A' ||
keycode <= '9' && keycode >= '0')
{
result = keycode;
}
else
{
is_printable = false;
switch (keycode)
{
case XK_Home: result = base_window::KEY_HOME; break;
case XK_Left: result = base_window::KEY_LEFT; break;
case XK_Right: result = base_window::KEY_RIGHT; break;
case XK_Down: result = base_window::KEY_DOWN; break;
case XK_Up: result = base_window::KEY_UP; break;
case XK_Prior: result = base_window::KEY_PAGE_UP; break;
case XK_Next: result = base_window::KEY_PAGE_DOWN; break;
case XK_End: result = base_window::KEY_END; break;
case XK_Escape: result = base_window::KEY_ESC; break;
case XK_KP_Delete: result = base_window::KEY_DELETE; break;
case XK_KP_Prior: result = base_window::KEY_PAGE_UP; break;
case XK_KP_Next: result = base_window::KEY_PAGE_DOWN; break;
case XK_F1: result = base_window::KEY_F1; break;
case XK_F2: result = base_window::KEY_F2; break;
case XK_F3: result = base_window::KEY_F3; break;
case XK_F4: result = base_window::KEY_F4; break;
case XK_F5: result = base_window::KEY_F5; break;
case XK_F6: result = base_window::KEY_F6; break;
case XK_F7: result = base_window::KEY_F7; break;
case XK_F8: result = base_window::KEY_F8; break;
case XK_F9: result = base_window::KEY_F9; break;
case XK_F10: result = base_window::KEY_F10; break;
case XK_F11: result = base_window::KEY_F11; break;
case XK_F12: result = base_window::KEY_F12; break;
case XK_Shift_L: result = base_window::KEY_SHIFT; break;
case XK_Shift_R: result = base_window::KEY_SHIFT; break;
case XK_Control_L: result = base_window::KEY_CTRL; break;
case XK_Control_R: result = base_window::KEY_CTRL; break;
case XK_Caps_Lock: result = base_window::KEY_CAPS_LOCK; break;
case XK_Alt_L: result = base_window::KEY_ALT; break;
case XK_Alt_R: result = base_window::KEY_ALT; break;
case XK_BackSpace: result = base_window::KEY_BACKSPACE; break;
case XK_Delete: result = base_window::KEY_DELETE; break;
case XK_Scroll_Lock: result = base_window::KEY_SCROLL_LOCK; break;
case XK_Pause: result = base_window::KEY_PAUSE; break;
case XK_Insert: result = base_window::KEY_INSERT; break;
case XK_KP_Insert: result = base_window::KEY_INSERT; break;
case XK_exclam:
is_printable = true;
result = '!'; break;
case XK_quotedbl:
is_printable = true;
result = '"'; break;
case XK_numbersign:
is_printable = true;
result = '#'; break;
case XK_dollar:
is_printable = true;
result = '$'; break;
case XK_percent:
is_printable = true;
result = '%'; break;
case XK_ampersand:
is_printable = true;
result = '&'; break;
case XK_apostrophe:
is_printable = true;
result = '\''; break;
case XK_parenleft:
is_printable = true;
result = '('; break;
case XK_parenright:
is_printable = true;
result = ')'; break;
case XK_asterisk:
is_printable = true;
result = '*'; break;
case XK_plus:
is_printable = true;
result = '+'; break;
case XK_comma:
is_printable = true;
result = ','; break;
case XK_minus:
is_printable = true;
result = '-'; break;
case XK_period:
is_printable = true;
result = '.'; break;
case XK_slash:
is_printable = true;
result = '/'; break;
case XK_colon:
is_printable = true;
result = ':'; break;
case XK_semicolon:
is_printable = true;
result = ';'; break;
case XK_less:
is_printable = true;
result = '<'; break;
case XK_equal:
is_printable = true;
result = '='; break;
case XK_greater:
is_printable = true;
result = '>'; break;
case XK_question:
is_printable = true;
result = '?'; break;
case XK_at:
is_printable = true;
result = '@'; break;
case XK_grave:
is_printable = true;
result = '`'; break;
case XK_underscore:
is_printable = true;
result = '_'; break;
case XK_asciicircum:
is_printable = true;
result = '^'; break;
case XK_bracketleft:
is_printable = true;
result = '['; break;
case XK_backslash:
is_printable = true;
result = '\\'; break;
case XK_bracketright:
is_printable = true;
result = ']'; break;
case XK_asciitilde:
is_printable = true;
result = '~'; break;
case XK_braceleft:
is_printable = true;
result = '{'; break;
case XK_bar:
is_printable = true;
result = '|'; break;
case XK_braceright:
is_printable = true;
result = '}'; break;
case XK_space:
is_printable = true;
result = ' '; break;
case XK_Return:
is_printable = true;
result = '\n'; break;
case XK_Tab:
is_printable = true;
result = '\t'; break;
case XK_KP_Divide:
is_printable = true;
result = '/'; break;
case XK_KP_Decimal:
is_printable = true;
result = '.'; break;
case XK_KP_Subtract:
is_printable = true;
result = '-'; break;
case XK_KP_Add:
is_printable = true;
result = '+'; break;
case XK_KP_Multiply:
is_printable = true;
result = '*'; break;
case XK_KP_Equal:
is_printable = true;
result = '='; break;
case XK_KP_0:
is_printable = true;
result = '0'; break;
case XK_KP_1:
is_printable = true;
result = '1'; break;
case XK_KP_2:
is_printable = true;
result = '2'; break;
case XK_KP_3:
is_printable = true;
result = '3'; break;
case XK_KP_4:
is_printable = true;
result = '4'; break;
case XK_KP_5:
is_printable = true;
result = '5'; break;
case XK_KP_6:
is_printable = true;
result = '6'; break;
case XK_KP_7:
is_printable = true;
result = '7'; break;
case XK_KP_8:
is_printable = true;
result = '8'; break;
case XK_KP_9:
is_printable = true;
result = '9'; break;
default:
return false;
}
}
return true;
}
// ----------------------------------------------------------------------------------------
void event_handler (
)
/*!
ensures
- will handle all events and event dispatching
!*/
{
try
{
std::vector<unsigned char> bitmap_buffer;
bool quit_event_loop = false;
while (quit_event_loop == false)
{
// get a lock on the window_table's mutex
auto_mutex window_table_locker(window_table.get_mutex());
XEvent ev;
while (XPending(disp) == 0){
window_table.get_mutex().unlock();
// wait until receiving X11 next event
struct pollfd pfd;
pfd.fd = ConnectionNumber(disp);
pfd.events = POLLIN | POLLPRI;
poll(&pfd, 1, -1);
window_table.get_mutex().lock();
}
XNextEvent(disp,&ev);
// pass events to input method.
// if this event is needed by input method, XFilterEvent returns True
if (XFilterEvent(&ev, None) == True){
continue;
}
// if this event is for one of the windows in the window_table
// then get that window out of the table and put it into win.
XAnyEvent* _ae = reinterpret_cast<XAnyEvent*>(&ev);
base_window** win_ = window_table[_ae->window];
base_window* win = 0;
if (win_)
win = *win_;
// ignore messages for unmapped windows
if (ev.type != MapNotify && win != 0)
{
if (win->is_mapped == false)
continue;
}
switch (ev.type)
{
case SelectionRequest:
{
Atom a_ct = XInternAtom(disp, "COMPOUND_TEXT", False);
XSelectionRequestEvent* req = reinterpret_cast<XSelectionRequestEvent*>(&ev.xselectionrequest);
XEvent respond;
if (req->target == XA_STRING)
{
XChangeProperty (disp,
req->requestor,
req->property,
XA_STRING,
8,
PropModeReplace,
reinterpret_cast<const unsigned char*>(convert_wstring_to_mbstring(clipboard).c_str()),
clipboard.size()+1);
respond.xselection.property=req->property;
}
else if (req->target == a_ct)
{
XChangeProperty (disp,
req->requestor,
req->property,
a_ct,
sizeof(wchar_t)*8,
PropModeReplace,
reinterpret_cast<const unsigned char*>(clipboard.c_str()),
clipboard.size()+1);
respond.xselection.property=req->property;
}
else
{
respond.xselection.property= None;
}
respond.xselection.type= SelectionNotify;
respond.xselection.display= req->display;
respond.xselection.requestor= req->requestor;
respond.xselection.selection=req->selection;
respond.xselection.target= req->target;
respond.xselection.time = req->time;
XSendEvent (disp, req->requestor,0,0,&respond);
XFlush (disp);
} break;
case MapNotify:
{
if (win == 0)
break;
win->is_mapped = true;
if (win->resizable == false)
{
XSizeHints* hints = XAllocSizeHints();
hints->flags = PMinSize|PMaxSize;
hints->min_width = win->width;
hints->max_width = win->width;
hints->max_height = win->height;
hints->min_height = win->height;
XSetNormalHints(gui_core_kernel_2_globals::disp,win->x11_stuff.hwnd,hints);
XFree(hints);
}
if (win->has_been_resized)
{
XResizeWindow(gui_core_kernel_2_globals::disp,win->x11_stuff.hwnd,win->width,win->height);
win->has_been_resized = false;
win->on_window_resized();
}
if (win->has_been_moved)
{
XMoveWindow(gui_core_kernel_2_globals::disp,win->x11_stuff.hwnd,win->x,win->y);
win->has_been_moved = false;
win->on_window_moved();
}
XFlush(gui_core_kernel_2_globals::disp);
} break;
case KeyPress:
{
XKeyPressedEvent* e = reinterpret_cast<XKeyPressedEvent*>(&ev);
if (win == 0)
break;
unsigned long state = 0;
bool shift = ((e->state & ShiftMask)!=0);
bool ctrl = ((e->state & ControlMask)!=0);
bool caps = ((e->state & LockMask)!=0);
if(shift)
state |= base_window::KBD_MOD_SHIFT;
if(ctrl)
state |= base_window::KBD_MOD_CONTROL;
if(caps)
state |= base_window::KBD_MOD_CAPS_LOCK;
if((e->state & alt_mask)!=0)
state |= base_window::KBD_MOD_ALT;
if((e->state & meta_mask)!=0)
state |= base_window::KBD_MOD_META;
if((e->state & num_lock_mask)!=0)
state |= base_window::KBD_MOD_NUM_LOCK;
if((e->state & scroll_lock_mask)!=0)
state |= base_window::KBD_MOD_SCROLL_LOCK;
KeySym key;
Status status;
if (win->x11_stuff.xic) {
std::wstring wstr;
wstr.resize(2);
int len = XwcLookupString(win->x11_stuff.xic,e,&wstr[0],wstr.size(),&key,&status);
if (status == XBufferOverflow){
wstr.resize(len);
len = XwcLookupString(win->x11_stuff.xic,e,&wstr[0],wstr.size(),&key,&status);
}
if (status == XLookupChars){
win->on_string_put(wstr);
}
} else {
char buffer[2];
XLookupString(e, buffer, sizeof(buffer), &key, NULL);
status = XLookupKeySym;
}
if (status == XLookupKeySym || status == XLookupBoth){
bool is_printable;
unsigned long result;
if (map_keys(key,shift,caps,result,is_printable))
{
// signal the keyboard event
win->on_keydown(result,is_printable,state);
}
}
} break;
case FocusIn:
{
if (win == 0)
break;
// signal the focus event
win->on_focus_gained();
} break;
case FocusOut:
{
if (win == 0)
break;
// signal the focus event
win->on_focus_lost();
} break;
case ButtonPress:
case ButtonRelease:
{
XButtonEvent* e = reinterpret_cast<XButtonEvent*>(&ev);
if (win == 0)
break;
unsigned long btn = base_window::NONE;
if (e->button == Button1)
btn = base_window::LEFT;
else if (e->button == Button3)
btn = base_window::RIGHT;
else if (e->button == Button2)
btn = base_window::MIDDLE;
// only send the event if this is a button we support
if (btn != (unsigned long)base_window::NONE)
{
unsigned long state = 0;
if (e->state & ControlMask)
state |= base_window::CONTROL;
if (e->state & Button1Mask)
state |= base_window::LEFT;
if (e->state & Button2Mask)
state |= base_window::MIDDLE;
if (e->state & Button3Mask)
state |= base_window::RIGHT;
if (e->state & ShiftMask)
state |= base_window::SHIFT;
if (ev.type == ButtonPress)
{
bool is_double_click = false;
if (win->last_click_button == btn &&
std::abs((long)win->last_click_x - (long)e->x) < 5 &&
std::abs((long)win->last_click_y - (long)e->y) < 5 &&
e->time - win->x11_stuff.last_click_time <= 400)
{
// this is a double click
is_double_click = true;
// set this to make sure the next click can't be
// interpreted as a double click
win->last_click_button = base_window::NONE;
}
else
{
win->last_click_button = btn;
win->last_click_x = e->x;
win->last_click_y = e->y;
win->x11_stuff.last_click_time = e->time;
}
// remove the clicked button from the state
state &= (~btn);
win->on_mouse_down(btn,state,e->x,e->y,is_double_click);
}
else
{
// remove the clicked button from the state
state &= (~btn);
win->on_mouse_up(btn,state,e->x,e->y);
}
}
else if (e->button == Button4 && ev.type == ButtonPress)
{
win->on_wheel_up();
}
else if (e->button == Button5 && ev.type == ButtonPress)
{
win->on_wheel_down();
}
} break;
case LeaveNotify:
{
if (win == 0)
break;
win->on_mouse_leave();
} break;
case EnterNotify:
{
if (win == 0)
break;
win->on_mouse_enter();
} break;
case MotionNotify:
{
XMotionEvent* e = reinterpret_cast<XMotionEvent*>(&ev);
if (win == 0)
break;
unsigned long state = 0;
if (e->state & ControlMask)
state |= base_window::CONTROL;
if (e->state & Button1Mask)
state |= base_window::LEFT;
if (e->state & Button2Mask)
state |= base_window::MIDDLE;
if (e->state & Button3Mask)
state |= base_window::RIGHT;
if (e->state & ShiftMask)
state |= base_window::SHIFT;
win->on_mouse_move(state,e->x,e->y);
} break;
case ConfigureNotify:
{
XConfigureEvent* e = reinterpret_cast<XConfigureEvent*>(&ev);
if (e->window == exit_window)
{
// this is the signal to quit the event handler
quit_event_loop = true;
break;
}
if (win == 0)
break;
if (win->width != e->width ||
win->height != e->height ||
win->has_been_resized)
{
win->has_been_resized = false;
// this is a resize
win->width = e->width;
win->height = e->height;
win->on_window_resized();
}
if (win->x != e->x ||
win->y != e->y ||
win->has_been_moved)
{
win->has_been_moved = false;
// this is a move
win->x = e->x;
win->y = e->y;
win->on_window_moved();
}
} break;
case ClientMessage:
{
XClientMessageEvent* e = reinterpret_cast<XClientMessageEvent*>(&ev);
if ((Atom)e->data.l[0] == delete_window)
{
if (win == 0)
break;
if (win->on_window_close() == base_window::DO_NOT_CLOSE_WINDOW)
{
DLIB_ASSERT(win->has_been_destroyed == false,
"\tYou called close_window() inside the on_window_close() event but"
<< "\n\tthen returned DO_NOT_CLOSE_WINDOW. You can do one or the other but not both."
<< "\n\tthis: " << win
);
// the client has decided not to close the window
// after all
}
else
{
if (window_table[e->window])
{
window_table.destroy(e->window);
XDestroyWindow(disp,e->window);
win->has_been_destroyed = true;
gui_core_kernel_2_globals::window_close_signaler.broadcast();
}
else
{
// in this case the window must have self destructed by
// calling delete this; so we don't have to do anything.
}
}
}
} break;
case Expose:
{
XExposeEvent* e = reinterpret_cast<XExposeEvent*>(&ev);
if (win == 0)
break;
// take all the expose events for this window out
XEvent etemp;
int x = e->x;
int y = e->y;
int width = e->width;
int height = e->height;
// What we are doing here with this loop is we are combining
// all of the Expose events for this window that are
// currently in the queue.
while (XCheckIfEvent(disp,&etemp,XCheckIfEventPredicate,reinterpret_cast<XPointer>(&(e->window))))
{
XExposeEvent* e2 = reinterpret_cast<XExposeEvent*>(&etemp);
if (e2->x < x)
{
width += x - e2->x;
x = e2->x;
}
if (e2->y < y)
{
height += y - e2->y;
y = e2->y;
}
if (e2->width + e2->x > width + x)
{
width = e2->width + e2->x - x;
}
if (e2->height + e2->y > height + y)
{
height = e2->height + e2->y - y;
}
}
// I'm not sure if this sort of thing can happen but
// if it does then just ignore this entire event.
if (width == 0 || height == 0)
{
break;
}
if (bitmap_buffer.size() < static_cast<unsigned long>(width*height*4))
bitmap_buffer.resize(width*height*4);
unsigned char* const bitmap = &bitmap_buffer[0];
unsigned char* const end = bitmap + width*height*4;
unsigned char* temp;
canvas c(bitmap,x,y,x+width-1,y+height-1);
win->paint(c);
// the user might have called win->close_window() and if they did
// then just stop right here. We don't want to paint the window.
if (win->has_been_destroyed)
break;
// if the color depth we are working with isn't 24bits then we need
// to transform our image into whatever it is supposed to be.
if (depth != 24)
{
// convert this image into an 8 bit image
unsigned int red_bits;
unsigned int green_bits;
unsigned int blue_bits;
if (depth != 16)
{
unsigned int bits = depth/3;
unsigned int extra = depth%3;
red_bits = bits;
green_bits = bits;
blue_bits = bits;
if (extra)
{
++red_bits;
--extra;
}
if (extra)
{
++green_bits;
}
}
else if (depth == 16)
{
red_bits = 5;
green_bits = 6;
blue_bits = 5;
}
if (depth == 16)
{
temp = bitmap;
unsigned char *red, *green, *blue;
while (temp != end)
{
blue = temp;
++temp;
green = temp;
++temp;
red = temp;
++temp;
++temp;
const unsigned long r = static_cast<unsigned long>(*red)>>(8-red_bits);
const unsigned long g = static_cast<unsigned long>(*green)>>(8-green_bits);
const unsigned long b = static_cast<unsigned long>(*blue)>>(8-blue_bits);
unsigned long color = (r<<(depth-red_bits))| (g<<(depth-red_bits-green_bits))| b;
*blue = (color>>0)&0xFF;
*green = (color>>8)&0xFF;
}
}
else if (depth < 24)
{
temp = bitmap;
unsigned char *red, *green, *blue;
while (temp != end)
{
blue = temp;
++temp;
green = temp;
++temp;
red = temp;
++temp;
++temp;
const unsigned long r = static_cast<unsigned long>(*red)>>(8-red_bits);
const unsigned long g = static_cast<unsigned long>(*green)>>(8-green_bits);
const unsigned long b = static_cast<unsigned long>(*blue)>>(8-blue_bits);
unsigned long color = (b<<(depth-blue_bits))| (g<<(depth-blue_bits-green_bits))| r;
*blue = (color>>0)&0xFF;
*green = (color>>8)&0xFF;
*red = (color>>16)&0xFF;
}
}
else if (depth > 24)
{
temp = bitmap;
unsigned char *red, *green, *blue, *four;
while (temp != end)
{
blue = temp;
++temp;
green = temp;
++temp;
red = temp;
++temp;
four = temp;
++temp;
const unsigned long r = static_cast<unsigned long>(*red)<<(red_bits-8);
const unsigned long g = static_cast<unsigned long>(*green)<<(green_bits-8);
const unsigned long b = static_cast<unsigned long>(*blue)<<(blue_bits-8);
unsigned long color = (b<<(depth-blue_bits))| (g<<(depth-blue_bits-green_bits))| r;
*blue = (color>>0)&0xFF;
*green = (color>>8)&0xFF;
*red = (color>>16)&0xFF;
*four = (color>>24)&0xFF;
}
}
} // if (depth != 24)
XImage img;
memset(&img,0,sizeof(img));
img.width = width;
img.height = height;
img.depth = depth;
img.data = reinterpret_cast<char*>(bitmap);
img.bitmap_bit_order = LSBFirst;
img.byte_order = LSBFirst;
img.format = ZPixmap;
img.bitmap_pad = 32;
img.bitmap_unit = 32;
img.bits_per_pixel = 32;
XInitImage(&img);
GC gc = XCreateGC(disp, e->window, 0, NULL);
XPutImage(disp,e->window,gc,&img,0,0,x,y,width,height);
XFreeGC(disp,gc);
} break;
} // switch (ev.type)
}
}
catch (std::exception& e)
{
dlog << LFATAL << "Exception thrown in event handler: " << e.what();
}
catch (...)
{
dlog << LFATAL << "Unknown exception thrown in event handler.";
}
}
// ----------------------------------------------------------------------------------------
class event_handler_thread : public threaded_object
{
public:
enum et_state
{
uninitialized,
initialized,
failure_to_init
};
et_state status;
event_handler_thread(
)
{
register_program_ending_handler(*this, &event_handler_thread::self_destruct);
status = uninitialized;
}
~event_handler_thread ()
{
using namespace gui_core_kernel_2_globals;
if (is_alive())
{
if (status != failure_to_init)
{
using namespace gui_core_kernel_2_globals;
XConfigureEvent event;
event.type = ConfigureNotify;
event.send_event = True;
event.display = disp;
event.window = exit_window;
event.x = 1;
XFlush(disp);
XPutBackEvent(disp,reinterpret_cast<XEvent*>(&event));
XFlush(disp);
// This should cause XNextEvent() to unblock so that it will see
// this ConfigureNotify event we are putting onto the event queue.
XSendEvent(disp,exit_window,False,0,reinterpret_cast<XEvent*>(&event));
XFlush(disp);
wait();
XCloseDisplay(disp);
}
else
{
wait();
}
}
delete &et_signaler;
delete &window_close_signaler;
delete &window_table;
}
void self_destruct (
)
{
delete this;
}
private:
void thread (
)
{
using namespace std;
using namespace dlib;
using namespace gui_core_kernel_2_globals;
try
{
/*
// causes dead-lock when using with XIM
if (XInitThreads() == 0)
{
dlog << LFATAL << "Unable to initialize threading support.";
// signal that an error has occurred
window_table.get_mutex().lock();
status = failure_to_init;
et_signaler.broadcast();
window_table.get_mutex().unlock();
return;
}
*/
window_table.get_mutex().lock();
disp = XOpenDisplay(NULL);
window_table.get_mutex().unlock();
if (disp == 0)
{
window_table.get_mutex(