support fullscreen and z-order better on Linux

This commit is contained in:
David Rose 2007-03-01 19:50:56 +00:00
parent 2527c1bb07
commit 3c2a1d95e0
4 changed files with 169 additions and 23 deletions

View File

@ -121,6 +121,12 @@ glxGraphicsPipe(const string &display) {
_net_wm_window_type = XInternAtom(_display, "_NET_WM_WINDOW_TYPE", false);
_net_wm_window_type_splash = XInternAtom(_display, "_NET_WM_WINDOW_TYPE_SPLASH", false);
_net_wm_window_type_fullscreen = XInternAtom(_display, "_NET_WM_WINDOW_TYPE_FULLSCREEN", false);
_net_wm_state = XInternAtom(_display, "_NET_WM_STATE", false);
_net_wm_state_fullscreen = XInternAtom(_display, "_NET_WM_STATE_FULLSCREEN", false);
_net_wm_state_above = XInternAtom(_display, "_NET_WM_STATE_ABOVE", false);
_net_wm_state_below = XInternAtom(_display, "_NET_WM_STATE_BELOW", false);
_net_wm_state_add = XInternAtom(_display, "_NET_WM_STATE_ADD", false);
_net_wm_state_remove = XInternAtom(_display, "_NET_WM_STATE_REMOVE", false);
}
////////////////////////////////////////////////////////////////////

View File

@ -114,6 +114,12 @@ public:
Atom _net_wm_window_type;
Atom _net_wm_window_type_splash;
Atom _net_wm_window_type_fullscreen;
Atom _net_wm_state;
Atom _net_wm_state_fullscreen;
Atom _net_wm_state_above;
Atom _net_wm_state_below;
Atom _net_wm_state_add;
Atom _net_wm_state_remove;
protected:
virtual PT(GraphicsOutput) make_output(const string &name,

View File

@ -65,6 +65,12 @@ glxGraphicsWindow(GraphicsPipe *pipe,
_net_wm_window_type = glx_pipe->_net_wm_window_type;
_net_wm_window_type_splash = glx_pipe->_net_wm_window_type_splash;
_net_wm_window_type_fullscreen = glx_pipe->_net_wm_window_type_fullscreen;
_net_wm_state = glx_pipe->_net_wm_state;
_net_wm_state_fullscreen = glx_pipe->_net_wm_state_fullscreen;
_net_wm_state_above = glx_pipe->_net_wm_state_above;
_net_wm_state_below = glx_pipe->_net_wm_state_below;
_net_wm_state_add = glx_pipe->_net_wm_state_add;
_net_wm_state_remove = glx_pipe->_net_wm_state_remove;
GraphicsWindowInputDevice device =
GraphicsWindowInputDevice::pointer_and_keyboard("keyboard/mouse");
@ -431,16 +437,23 @@ set_properties_now(WindowProperties &properties) {
glxGraphicsPipe *glx_pipe;
DCAST_INTO_V(glx_pipe, _pipe);
// We'll pass some property requests on as a window manager hint.
WindowProperties wm_properties = _properties;
wm_properties.add_properties(properties);
// The window title may be changed by issuing another hint request.
// Assume this will be honored.
if (properties.has_title()) {
WindowProperties wm_properties;
wm_properties.set_title(properties.get_title());
_properties.set_title(properties.get_title());
set_wm_properties(wm_properties);
properties.clear_title();
}
// Ditto for fullscreen.
if (properties.has_fullscreen()) {
_properties.set_fullscreen(properties.get_fullscreen());
properties.clear_fullscreen();
}
// The size and position of an already-open window are changed via
// explicit X calls. These may still get intercepted by the window
// manager. Rather than changing _properties immediately, we'll
@ -460,9 +473,31 @@ set_properties_now(WindowProperties &properties) {
value_mask |= (CWWidth | CWHeight);
properties.clear_size();
}
if (properties.has_z_order()) {
// We'll send the classing stacking request through the standard
// interface, for users of primitive window managers; but we'll
// also send it as a window manager hint, for users of modern
// window managers.
switch (properties.get_z_order()) {
case WindowProperties::Z_bottom:
changes.stack_mode = Below;
break;
case WindowProperties::Z_normal:
changes.stack_mode = TopIf;
break;
case WindowProperties::Z_top:
changes.stack_mode = Above;
break;
}
value_mask |= (CWStackMode);
properties.clear_z_order();
}
if (value_mask != 0) {
XConfigureWindow(_display, _xwindow, value_mask, &changes);
XReconfigureWMWindow(_display, _xwindow, _screen, value_mask, &changes);
// Don't draw anything until this is done reconfiguring.
_awaiting_configure = true;
@ -478,6 +513,8 @@ set_properties_now(WindowProperties &properties) {
}
properties.clear_cursor_hidden();
}
set_wm_properties(wm_properties, true);
}
////////////////////////////////////////////////////////////////////
@ -600,7 +637,7 @@ open_window() {
<< "failed to create X window.\n";
return false;
}
set_wm_properties(_properties);
set_wm_properties(_properties, false);
// We don't specify any fancy properties of the XIC. It would be
// nicer if we could support fancy IM's that want preedit callbacks,
@ -645,9 +682,13 @@ open_window() {
// specified directly by the application; they must be
// requested via the window manager, which may or may
// not choose to honor the request.
//
// If already_mapped is true, the window has already
// been mapped (manifested) on the display. This means
// we may need to use a different action in some cases.
////////////////////////////////////////////////////////////////////
void glxGraphicsWindow::
set_wm_properties(const WindowProperties &properties) {
set_wm_properties(const WindowProperties &properties, bool already_mapped) {
// Name the window if there is a name
XTextProperty window_name;
XTextProperty *window_name_p = (XTextProperty *)NULL;
@ -698,6 +739,40 @@ set_wm_properties(const WindowProperties &properties) {
wm_hints_p->flags = StateHint;
}
// Two competing window manager interfaces have evolved. One of
// them allows to set certain properties as a "type"; the other one
// as a "state". We'll try to honor both.
static const int max_type_data = 32;
PN_int32 type_data[max_type_data];
int next_type_data = 0;
static const int max_state_data = 32;
PN_int32 state_data[max_state_data];
int next_state_data = 0;
static const int max_set_data = 32;
class SetAction {
public:
inline SetAction() { }
inline SetAction(Atom state, Atom action) : _state(state), _action(action) { }
Atom _state;
Atom _action;
};
SetAction set_data[max_set_data];
int next_set_data = 0;
if (properties.get_fullscreen()) {
// For a "fullscreen" request, we pass this through, hoping the
// window manager will support EWMH.
type_data[next_type_data++] = _net_wm_window_type_fullscreen;
// We also request it as a state.
state_data[next_state_data++] = _net_wm_state_fullscreen;
set_data[next_set_data++] = SetAction(_net_wm_state_fullscreen, _net_wm_state_add);
} else {
set_data[next_set_data++] = SetAction(_net_wm_state_fullscreen, _net_wm_state_remove);
}
// If we asked for a window without a border, there's no excellent
// way to arrange that. For users whose window managers follow the
// EWMH specification, we can ask for a "splash" screen, which is
@ -712,25 +787,77 @@ set_wm_properties(const WindowProperties &properties) {
class_hints_p = XAllocClassHint();
class_hints_p->res_class = "Undecorated";
long data[2];
data[0] = _net_wm_window_type_splash;
data[1] = None;
XChangeProperty(_display, _xwindow, _net_wm_window_type,
XA_ATOM, 32, PropModeReplace,
(unsigned char *)data, 1);
if (!properties.get_fullscreen()) {
type_data[next_type_data++] = _net_wm_window_type_splash;
}
}
if (properties.get_fullscreen()) {
// For a "fullscreen" request, we pass this through, hoping the
// window manager will support EWMH.
long data[2];
data[0] = _net_wm_window_type_fullscreen;
data[1] = None;
if (properties.has_z_order()) {
switch (properties.get_z_order()) {
case WindowProperties::Z_bottom:
state_data[next_state_data++] = _net_wm_state_below;
set_data[next_set_data++] = SetAction(_net_wm_state_below, _net_wm_state_add);
set_data[next_set_data++] = SetAction(_net_wm_state_above, _net_wm_state_remove);
break;
case WindowProperties::Z_normal:
set_data[next_set_data++] = SetAction(_net_wm_state_below, _net_wm_state_remove);
set_data[next_set_data++] = SetAction(_net_wm_state_above, _net_wm_state_remove);
break;
case WindowProperties::Z_top:
state_data[next_state_data++] = _net_wm_state_above;
set_data[next_set_data++] = SetAction(_net_wm_state_below, _net_wm_state_remove);
set_data[next_set_data++] = SetAction(_net_wm_state_above, _net_wm_state_add);
break;
}
}
nassertv(next_type_data < max_type_data);
nassertv(next_state_data < max_state_data);
nassertv(next_set_data < max_set_data);
XChangeProperty(_display, _xwindow, _net_wm_window_type,
XA_ATOM, 32, PropModeReplace,
(unsigned char *)data, 1);
(unsigned char *)type_data, next_type_data);
// Request the state properties all at once.
XChangeProperty(_display, _xwindow, _net_wm_state,
XA_ATOM, 32, PropModeReplace,
(unsigned char *)state_data, next_state_data);
if (!already_mapped) {
// We have to request state changes differently when the window
// has been mapped. To do this, we need to send a client message
// to the root window for each change.
// This doesn't appear to be working for me, though I think I'm
// doing everything right. There's no feedback mechanism,
// however, so it's impossible to tell whether I'm actually doing
// something wrong, or whether this feature is simply not
// supported in my current window manager. I'll leave it here for
// now.
glxGraphicsPipe *glx_pipe;
DCAST_INTO_V(glx_pipe, _pipe);
for (int i = 0; i < next_set_data; ++i) {
XClientMessageEvent event;
memset(&event, 0, sizeof(event));
event.type = ClientMessage;
event.send_event = True;
event.display = _display;
event.window = _xwindow;
event.message_type = _net_wm_state;
event.format = 32;
event.data.l[0] = set_data[i]._action;
event.data.l[1] = set_data[i]._state;
event.data.l[2] = 0;
event.data.l[3] = 1;
XSendEvent(_display, glx_pipe->get_root(), True, 0, (XEvent *)&event);
}
}
XSetWMProperties(_display, _xwindow, window_name_p, window_name_p,

View File

@ -54,7 +54,8 @@ protected:
virtual bool open_window();
private:
void set_wm_properties(const WindowProperties &properties);
void set_wm_properties(const WindowProperties &properties,
bool already_mapped);
#ifdef HAVE_GLXFBCONFIG
void setup_colormap(GLXFBConfig fbconfig);
@ -82,6 +83,12 @@ private:
Atom _net_wm_window_type;
Atom _net_wm_window_type_splash;
Atom _net_wm_window_type_fullscreen;
Atom _net_wm_state;
Atom _net_wm_state_fullscreen;
Atom _net_wm_state_above;
Atom _net_wm_state_below;
Atom _net_wm_state_add;
Atom _net_wm_state_remove;
public:
static TypeHandle get_class_type() {