fix bug that caused wrong window position in X11

This commit is contained in:
Jinhao 2018-08-05 16:41:39 +08:00
parent 1339658c4c
commit 1ff1c55592
10 changed files with 237 additions and 88 deletions

View File

@ -51,6 +51,13 @@ namespace nana
top_left, top_right, bottom_left, bottom_right
};
enum class window_relationship
{
owner, ///< Owner window.
parent, ///< Parent window.
either_po ///< One between owner and parent.
};
enum class bground_mode
{
none,

View File

@ -1,7 +1,7 @@
/*
* Platform Implementation
* Nana C++ Library(http://www.nanapro.org)
* Copyright(C) 2003-2017 Jinhao(cnjinhao@hotmail.com)
* Copyright(C) 2003-2018 Jinhao(cnjinhao@hotmail.com)
*
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
@ -76,8 +76,8 @@ namespace detail
static native_string_type window_caption(native_window_type);
static void capture_window(native_window_type, bool);
static nana::point cursor_position();
static native_window_type get_owner_window(native_window_type);
static native_window_type parent_window(native_window_type);
static native_window_type get_window(native_window_type wd, window_relationship);
static native_window_type parent_window(native_window_type child, native_window_type new_parent, bool returns_previous);
//For Caret
static void caret_create(native_window_type, const ::nana::size&);

View File

@ -170,7 +170,8 @@ namespace nana
}
/// Base class of all the classes defined as a widget window. Defaultly a widget_tag
template<typename Category, typename DrawerTrigger, typename Events = ::nana::general_events, typename Scheme = ::nana::widget_geometrics>
template<typename Category, typename DrawerTrigger, typename Events = ::nana::general_events, typename Scheme = ::nana::widget_geometrics,
typename = typename std::enable_if<std::is_base_of<::nana::drawer_trigger, DrawerTrigger>::value>::type> //type DrawerTrigger must be derived from nana::drawer_trigger
class widget_object: public detail::widget_base
{
protected:
@ -182,7 +183,9 @@ namespace nana
widget_object()
: events_{ std::make_shared<Events>() },
scheme_{ API::dev::make_scheme<Scheme>() }
{}
{
static_assert(std::is_base_of<::nana::drawer_trigger, DrawerTrigger>::value, "The type DrawerTrigger must be derived from nana::drawer_trigger");
}
~widget_object()
{
@ -291,7 +294,7 @@ namespace nana
};//end class widget_object
/// Base class of all the classes defined as a non-graphics-buffer widget window. The second template parameter DrawerTrigger is always ignored.\see nana::panel
template<typename DrawerTrigger, typename Events, typename Scheme>
template<typename DrawerTrigger, typename Events, typename Scheme> //type DrawerTrigger must be derived from nana::drawer_trigger
class widget_object<category::lite_widget_tag, DrawerTrigger, Events, Scheme>: public detail::widget_base
{
protected:
@ -302,7 +305,9 @@ namespace nana
widget_object()
: events_{ std::make_shared<Events>() }, scheme_{ API::dev::make_scheme<scheme_type>() }
{}
{
static_assert(std::is_base_of<::nana::drawer_trigger, DrawerTrigger>::value, "The type DrawerTrigger must be derived from nana::drawer_trigger");
}
~widget_object()
{
@ -355,7 +360,7 @@ namespace nana
/// Base class of all the classes defined as a root window. \see nana::form
template<typename DrawerTrigger, typename Events, typename Scheme>
template<typename DrawerTrigger, typename Events, typename Scheme> //type DrawerTrigger must be derived from nana::drawer_trigger
class widget_object<category::root_tag, DrawerTrigger, Events, Scheme>: public detail::widget_base
{
protected:
@ -367,10 +372,12 @@ namespace nana
widget_object()
: widget_object(nullptr, false, API::make_center(300, 150), appearance(), this)
{
static_assert(std::is_base_of<::nana::drawer_trigger, DrawerTrigger>::value, "The type DrawerTrigger must be derived from nana::drawer_trigger");
}
widget_object(window owner, bool nested, const rectangle& r = {}, const appearance& apr = {})
{
static_assert(std::is_base_of<::nana::drawer_trigger, DrawerTrigger>::value, "The type DrawerTrigger must be derived from nana::drawer_trigger");
handle_ = API::dev::create_window(owner, nested, r, apr, this);
_m_bind_and_attach();
}

View File

@ -486,6 +486,7 @@ namespace detail
atombase_.wm_protocols = ::XInternAtom(display_, "WM_PROTOCOLS", False);
atombase_.wm_change_state = ::XInternAtom(display_, "WM_CHANGE_STATE", False);
atombase_.wm_delete_window = ::XInternAtom(display_, "WM_DELETE_WINDOW", False);
atombase_.net_frame_extents = ::XInternAtom(display_, "_NET_FRAME_EXTENTS", False);
atombase_.net_wm_state = ::XInternAtom(display_, "_NET_WM_STATE", False);
atombase_.net_wm_state_skip_taskbar = ::XInternAtom(display_, "_NET_WM_STATE_SKIP_TASKBAR", False);
atombase_.net_wm_state_fullscreen = ::XInternAtom(display_, "_NET_WM_STATE_FULLSCREEN", False);

View File

@ -132,6 +132,7 @@ namespace detail
Atom wm_change_state;
Atom wm_delete_window;
//ext
Atom net_frame_extents;
Atom net_wm_state;
Atom net_wm_state_skip_taskbar;
Atom net_wm_state_fullscreen;

View File

@ -308,11 +308,11 @@ namespace nana
{
if (pi_data_->menu.window && (pi_data_->menu.window != wd))
{
wd = native_interface::get_owner_window(wd);
wd = native_interface::get_window(wd, window_relationship::owner);
while (wd)
{
if (wd != pi_data_->menu.window)
wd = native_interface::get_owner_window(wd);
wd = native_interface::get_window(wd, window_relationship::owner);
else
return false;
}
@ -329,7 +329,7 @@ namespace nana
erase_menu(true);
pi_data_->menu.window = menu_wd;
pi_data_->menu.owner = native_interface::get_owner_window(menu_wd);
pi_data_->menu.owner = native_interface::get_window(menu_wd, window_relationship::owner);
pi_data_->menu.has_keyboard = has_keyboard;
}
}

View File

@ -26,6 +26,9 @@ namespace nana
{
namespace detail
{
//Declarations of helper functions defined in native_window_interface.cpp
void x11_apply_exposed_position(native_window_type wd);
#pragma pack(1)
union event_mask
{
@ -905,6 +908,9 @@ namespace detail
break;
case MapNotify:
case UnmapNotify:
if(xevent.type == MapNotify)
x11_apply_exposed_position(native_window);
brock.event_expose(msgwnd, (xevent.type == MapNotify));
context.platform.motion_window = nullptr;
break;
@ -1281,7 +1287,7 @@ namespace detail
if(condition_wd && is_modal)
{
native_window_type modal = reinterpret_cast<core_window_t*>(condition_wd)->root;
owner_native = native_interface::get_owner_window(modal);
owner_native = native_interface::get_window(modal, window_relationship::owner);
if(owner_native)
{
native_interface::enable_window(owner_native, false);

View File

@ -1,7 +1,7 @@
/*
* Platform Implementation
* Nana C++ Library(http://www.nanapro.org)
* Copyright(C) 2003-2017 Jinhao(cnjinhao@hotmail.com)
* Copyright(C) 2003-2018 Jinhao(cnjinhao@hotmail.com)
*
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
@ -129,6 +129,140 @@ namespace nana{
{
nana::detail::platform_spec & spec = nana::detail::platform_spec::instance();
}
struct frame_extents
{
long left;
long right;
long top;
long bottom;
};
frame_extents x11_frame_extents(Window wd)
{
frame_extents fm_extents;
Atom type;
int format;
unsigned long len, bytes_left = 0;
unsigned char *data;
if(Success == ::XGetWindowProperty(restrict::spec.open_display(), wd,
restrict::spec.atombase().net_frame_extents, 0, 16, 0,
XA_CARDINAL, &type, &format,
&len, &bytes_left, &data))
{
if(type != None && len == 4)
{
fm_extents.left = ((long*)data)[0];
fm_extents.right = ((long*)data)[1];
fm_extents.top = ((long*)data)[2];
fm_extents.bottom = ((long*)data)[3];
}
::XFree(data);
}
return fm_extents;
}
//The XMoveWindow and XMoveResizeWindow don't take effect if the specified window is
//unmapped, and the members x and y of XSetSizeHints is obsoluted. So the position that
//set to a unmapped windows should be kept and use the position when the window is mapped.
std::map<Window, ::nana::point> exposed_positions; //locked by platform_scope_guard
//Returns the parent window.
//It may return a decoration frame window if the requested window is a top level and WM is a
//reparenting window manager.
native_window_type x11_parent_window(native_window_type wd)
{
Window root;
Window parent;
Window * children;
unsigned size;
platform_scope_guard lock;
if(0 != ::XQueryTree(restrict::spec.open_display(), reinterpret_cast<Window>(wd),
&root, &parent, &children, &size))
{
::XFree(children);
return reinterpret_cast<native_window_type>(parent);
}
return nullptr;
}
// Revise the position for moving window. Because the window is moved depending on
//implementation of Window Manager. A reparenting window may not be moved the origin to
//the specified location. it may be moved the left-top corner to the specified location.
void x11_revise_position(native_window_type wd, int &x, int& y)
{
auto const disp = restrict::spec.open_display();
auto const owner = reinterpret_cast<Window>(restrict::spec.get_owner(wd));
auto const root_wd = restrict::spec.root_window();
Window decoration_wd = 0;
if(owner)
{
Window child;
if(owner != root_wd)
{
::XTranslateCoordinates(disp, owner, root_wd,
x, y, &x, &y, &child);
}
decoration_wd = reinterpret_cast<Window>(x11_parent_window(wd));
if((decoration_wd == owner) || (decoration_wd == root_wd))
decoration_wd = 0;
}
if(decoration_wd)
{
auto fm_extents = x11_frame_extents(reinterpret_cast<Window>(wd));
XWindowAttributes attr;
::XGetWindowAttributes(disp, reinterpret_cast<Window>(wd), &attr);
x += attr.x - fm_extents.left;
y += attr.y - fm_extents.top;
}
}
void x11_apply_exposed_position(native_window_type wd)
{
nana::detail::platform_scope_guard lock;
auto i = exposed_positions.find(reinterpret_cast<Window>(wd));
if(i == exposed_positions.cend())
return;
native_interface::move_window(wd, i->second.x, i->second.y);
exposed_positions.erase(i);
}
namespace x11_wait
{
static Bool configure(Display *disp, XEvent *evt, char *arg)
{
return disp && evt && arg && (evt->type == ConfigureNotify) && (evt->xconfigure.window == *reinterpret_cast<Window*>(arg));
}
static Bool map(Display *disp, XEvent *evt, char *arg)
{
return disp && evt && arg && (evt->type == MapNotify) && (evt->xmap.window == *reinterpret_cast<Window*>(arg));
}
static Bool unmap(Display *disp, XEvent *evt, char *arg)
{
return disp && evt && arg && (evt->type == MapNotify) && (evt->xunmap.window == *reinterpret_cast<Window*>(arg));
}
}
static void x11_wait_for(Window wd, Bool(*pred_fn)(Display*, XEvent*, char*))
{
XEvent dummy;
::XPeekIfEvent(restrict::spec.open_display(), &dummy, pred_fn, reinterpret_cast<XPointer>(&wd));
}
#endif
//struct native_interface
@ -196,13 +330,6 @@ namespace nana{
return rectangle{ primary_monitor_size() };
}
#ifdef NANA_X11
//The XMoveWindow and XMoveResizeWindow don't take effect if the specified window is
//unmapped, and the members x and y of XSetSizeHints is obsoluted. So the position that
//set to a unmapped windows should be kept and use the position when the window is mapped.
std::map<Window, ::nana::point> exposed_positions; //locked by platform_scope_guard
#endif
//platform-dependent
native_interface::window_result native_interface::create_window(native_window_type owner, bool nested, const rectangle& r, const appearance& app)
{
@ -267,7 +394,7 @@ namespace nana{
XSetWindowAttributes win_attr;
unsigned long attr_mask = CWBackPixmap | CWBackPixel | CWBorderPixel |
CWWinGravity | CWBitGravity | CWColormap | CWEventMask;
CWColormap | CWEventMask;
Display * disp = restrict::spec.open_display();
win_attr.colormap = restrict::spec.colormap();
@ -276,8 +403,6 @@ namespace nana{
win_attr.background_pixel = 0xFFFFFF;
win_attr.border_pixmap = None;
win_attr.border_pixel = 0x0;
win_attr.bit_gravity = 0;
win_attr.win_gravity = NorthWestGravity;
win_attr.backing_store = 0;
win_attr.backing_planes = 0;
win_attr.backing_pixel = 0;
@ -424,7 +549,7 @@ namespace nana{
XSetWindowAttributes win_attr;
unsigned long attr_mask = CWBackPixmap | CWBackPixel | CWBorderPixel |
CWWinGravity | CWBitGravity | CWColormap | CWEventMask;
CWColormap | CWEventMask | CWOverrideRedirect;
Display * disp = restrict::spec.open_display();
win_attr.colormap = restrict::spec.colormap();
@ -433,15 +558,12 @@ namespace nana{
win_attr.background_pixel = 0xFFFFFF;
win_attr.border_pixmap = None;
win_attr.border_pixel = 0x0;
win_attr.bit_gravity = 0;
win_attr.win_gravity = NorthWestGravity;
win_attr.backing_store = 0;
win_attr.backing_planes = 0;
win_attr.backing_pixel = 0;
win_attr.colormap = restrict::spec.colormap();
win_attr.override_redirect = True;
attr_mask |= CWOverrideRedirect;
nana::point pos(r.x, r.y);
win_attr.event_mask = ButtonPressMask | ButtonReleaseMask | PointerMotionMask | KeyPressMask | KeyReleaseMask | ExposureMask | StructureNotifyMask | EnterWindowMask | LeaveWindowMask | FocusChangeMask;
@ -671,21 +793,21 @@ namespace nana{
{
::XMapWindow(disp, reinterpret_cast<Window>(wd));
auto i = exposed_positions.find(reinterpret_cast<Window>(wd));
if(i != exposed_positions.end())
{
::XMoveWindow(disp, reinterpret_cast<Window>(wd), i->second.x, i->second.y);
exposed_positions.erase(i);
}
//Wait for the mapping notify to update the local attribute of visibility so that
//the followed window_visible() call can return the updated visibility value.
x11_wait_for(reinterpret_cast<Window>(wd), x11_wait::map);
Window grab = restrict::spec.grab(0);
if(grab == reinterpret_cast<Window>(wd))
capture_window(wd, true);
}
else
{
::XUnmapWindow(disp, reinterpret_cast<Window>(wd));
::XFlush(disp);
//Wait for the mapping notify to update the local attribute of visibility so that
//the followed window_visible() call can return the updated visibility value.
x11_wait_for(reinterpret_cast<Window>(wd), x11_wait::unmap);
}
}
static_cast<void>(active); //eliminate unused parameter compiler warning.
#endif
@ -847,19 +969,24 @@ namespace nana{
}
return nana::point(r.left, r.top);
#elif defined(NANA_X11)
int x, y;
nana::detail::platform_scope_guard psg;
Window coord_wd = reinterpret_cast<Window>(restrict::spec.get_owner(wd));
if(!coord_wd)
point scr_pos;
nana::detail::platform_scope_guard lock;
auto coord_wd = restrict::spec.get_owner(wd);
if(coord_wd)
{
coord_wd = reinterpret_cast<Window>(parent_window(wd));
if(!coord_wd)
coord_wd = restrict::spec.root_window();
//wd is a top level window. It returns the position of its decoration window.
auto decr = x11_parent_window(wd);
if(decr != coord_wd)
wd = decr;
}
else
coord_wd = get_window(wd, window_relationship::parent);
Window child;
if(True == ::XTranslateCoordinates(restrict::spec.open_display(), reinterpret_cast<Window>(wd), coord_wd, 0, 0, &x, &y, &child))
return nana::point(x, y);
return nana::point(0, 0);
::XTranslateCoordinates(restrict::spec.open_display(), reinterpret_cast<Window>(wd), reinterpret_cast<Window>(coord_wd), 0, 0, &scr_pos.x, &scr_pos.y, &child);
return scr_pos;
#endif
}
@ -890,21 +1017,21 @@ namespace nana{
#elif defined(NANA_X11)
Display * disp = restrict::spec.open_display();
nana::detail::platform_scope_guard psg;
Window owner = reinterpret_cast<Window>(restrict::spec.get_owner(wd));
if(owner)
{
Window child;
::XTranslateCoordinates(disp, owner, restrict::spec.root_window(),
x, y, &x, &y, &child);
}
nana::detail::platform_scope_guard lock;
XWindowAttributes attr;
::XGetWindowAttributes(disp, reinterpret_cast<Window>(wd), &attr);
if(attr.map_state == IsUnmapped)
exposed_positions[reinterpret_cast<Window>(wd)] = ::nana::point{x, y};
x11_revise_position(wd, x, y);
::XMoveWindow(disp, reinterpret_cast<Window>(wd), x, y);
//Wait for the configuration notify to update the local attribute of position so that
//the followed window_position() call can return the updated position value.
x11_wait_for(reinterpret_cast<Window>(wd), x11_wait::configure);
#endif
}
@ -954,16 +1081,6 @@ namespace nana{
else
hints.flags = 0;
Window owner = reinterpret_cast<Window>(restrict::spec.get_owner(wd));
int x = r.x;
int y = r.y;
if(owner)
{
Window child;
::XTranslateCoordinates(disp, owner, restrict::spec.root_window(),
r.x, r.y, &x, &y, &child);
}
XWindowAttributes attr;
::XGetWindowAttributes(disp, reinterpret_cast<Window>(wd), &attr);
if(attr.map_state == IsUnmapped)
@ -972,13 +1089,24 @@ namespace nana{
hints.width = r.width;
hints.height = r.height;
exposed_positions[reinterpret_cast<Window>(wd)] = point{x, y};
exposed_positions[reinterpret_cast<Window>(wd)] = r.position();
}
if(hints.flags)
::XSetWMNormalHints(disp, reinterpret_cast<Window>(wd), &hints);
int x = r.x;
int y = r.y;
x11_revise_position(wd, x, y);
::XMoveResizeWindow(disp, reinterpret_cast<Window>(wd), x, y, r.width, r.height);
//Wait for the configuration notify to update the local attribute of position so that
//the followed window_position() call can return the updated position value.
//It seems that XMoveResizeWindow doesn't need x11_wait_for. But x11_wait_for is still called
//to make sure the local attribute is updated.
x11_wait_for(reinterpret_cast<Window>(wd), x11_wait::configure);
return true;
#endif
}
@ -1096,6 +1224,10 @@ namespace nana{
::XSetWMNormalHints(disp, reinterpret_cast<Window>(wd), &hints);
}
::XResizeWindow(disp, reinterpret_cast<Window>(wd), sz.width, sz.height);
//It seems that XResizeWindow doesn't need x11_wait_for. But x11_wait_for is still called
//to make sure the local attribute is updated.
x11_wait_for(reinterpret_cast<Window>(wd), x11_wait::configure);
return true;
#endif
}
@ -1231,34 +1363,29 @@ namespace nana{
#endif
}
native_window_type native_interface::get_owner_window(native_window_type wd)
{
#if defined(NANA_WINDOWS)
return reinterpret_cast<native_window_type>(::GetWindow(reinterpret_cast<HWND>(wd), GW_OWNER));
#elif defined(NANA_X11)
return restrict::spec.get_owner(wd);
#endif
}
native_window_type native_interface::parent_window(native_window_type wd)
native_window_type native_interface::get_window(native_window_type wd, window_relationship rsp)
{
#ifdef NANA_WINDOWS
return reinterpret_cast<native_window_type>(::GetParent(reinterpret_cast<HWND>(wd)));
if(window_relationship::either_po == rsp)
return reinterpret_cast<native_window_type>(::GetParent(reinterpret_cast<HWND>(wd)));
else if(window_relationship::parent == rsp)
return reinterpret_cast<native_window_type>(::GetAncestor(reinterpret_cast<HWND>(wd), GA_PARENT));
else if(window_relationship::owner == rsp)
return reinterpret_cast<native_window_type>(::GetWindow(reinterpret_cast<HWND>(wd), GW_OWNER));
#elif defined(NANA_X11)
Window root;
Window parent;
Window * children;
unsigned size;
platform_scope_guard lock;
if(0 != ::XQueryTree(restrict::spec.open_display(), reinterpret_cast<Window>(wd),
&root, &parent, &children, &size))
auto owner = restrict::spec.get_owner(wd);
if(window_relationship::either_po == rsp)
{
::XFree(children);
return reinterpret_cast<native_window_type>(parent);
if(owner)
return owner;
}
return nullptr;
else if(window_relationship::owner == rsp)
return owner;
return x11_parent_window(wd);
#endif
}
@ -1279,7 +1406,7 @@ namespace nana{
platform_scope_guard lock;
if(returns_previous)
prev = parent_window(child);
prev = get_window(child, window_relationship::either_po);
if(native_window_type{} == new_parent)
new_parent = reinterpret_cast<native_window_type>(restrict::spec.root_window());

View File

@ -669,7 +669,7 @@ namespace API
internal_scope_guard lock;
if(restrict::wd_manager().available(iwd) && (iwd->other.category == category::flags::root))
{
auto owner = interface_type::get_owner_window(iwd->root);
auto owner = interface_type::get_window(iwd->root, window_relationship::owner);
if(owner)
return reinterpret_cast<window>(restrict::wd_manager().root(owner));
}

View File

@ -267,7 +267,7 @@ namespace nana{
//When the group is resized, the drawing is called before moving the caption, but
//the drawing of group requires the lastest position of caption for gradual rectangle.
//For the requirement, a move event handler is required for listning the change of caption's position.
impl_->caption.events().move([this](const arg_move& arg){
impl_->caption.events().move([this](const arg_move&){
if (align::left != impl_->caption_align)
API::refresh_window(*this);
});