Support for full screen over multiple monitors.

The API follows that of EWMH, which gives you a good control
over which monitors to use.


git-svn-id: file:///fltk/svn/fltk/branches/branch-1.3@10189 ea41ed52-d2ee-0310-a9c1-e6b18d33e121
This commit is contained in:
Pierre Ossman 2014-06-11 09:10:53 +00:00
parent f27bde8316
commit 0cfc2554c6
7 changed files with 226 additions and 32 deletions

View File

@ -49,7 +49,7 @@ class Fl_X;
class FL_EXPORT Fl_Window : public Fl_Group {
static char *default_xclass_;
// Note: we must use separate statements for each of the following 4 variables,
// Note: we must use separate statements for each of the following 8 variables,
// with the static attribute, otherwise MS VC++ 2008/2010 complains :-(
// AlbrechtS 04/2012
#if FLTK_ABI_VERSION < 10301
@ -68,6 +68,22 @@ class FL_EXPORT Fl_Window : public Fl_Group {
static // when these members are static, ABI compatibility with 1.3.0 is respected
#endif
int no_fullscreen_h;
#if FLTK_ABI_VERSION < 10303
static // when these members are static, ABI compatibility with 1.3.2 is respected
#endif
int fullscreen_screen_top;
#if FLTK_ABI_VERSION < 10303
static // when these members are static, ABI compatibility with 1.3.2 is respected
#endif
int fullscreen_screen_bottom;
#if FLTK_ABI_VERSION < 10303
static // when these members are static, ABI compatibility with 1.3.2 is respected
#endif
int fullscreen_screen_left;
#if FLTK_ABI_VERSION < 10303
static // when these members are static, ABI compatibility with 1.3.2 is respected
#endif
int fullscreen_screen_right;
friend class Fl_X;
Fl_X *i; // points at the system-specific stuff
@ -402,13 +418,15 @@ public:
*/
void show(int argc, char **argv);
/**
Makes the window completely fill the screen, without any window
manager border visible. You must use fullscreen_off() to undo
this.
Makes the window completely fill one or more screens, without any
window manager border visible. You must use fullscreen_off() to
undo this.
\note On some platforms, this can result in the keyboard being
grabbed. The window may also be recreated, meaning hide() and
show() will be called.
\see void Fl_Window::fullscreen_screens()
*/
void fullscreen();
/**
@ -424,6 +442,17 @@ public:
Returns non zero if FULLSCREEN flag is set, 0 otherwise.
*/
unsigned int fullscreen_active() const { return flags() & FULLSCREEN; }
/**
Sets which screens should be used when this window is in fullscreen
mode. The window will be resized to the top of the screen with index
\p top, the bottom of the screen with index \p bottom, etc.
If this method is never called, or if any argument is < 0, then the
window will be resized to fill the screen it is currently on.
\see void Fl_Window::fullscreen()
*/
void fullscreen_screens(int top, int bottom, int left, int right);
/**
Iconifies the window. If you call this when shown() is false
it will show() it as an icon. If the window is already

View File

@ -79,6 +79,7 @@ public:
static Fl_X* i(const Fl_Window* w) {return w->i;}
static int fake_X_wm(const Fl_Window* w,int &X, int &Y,
int &bt,int &bx,int &by);
void make_fullscreen(int X, int Y, int W, int H);
void setwindow(Fl_Window* wi) {w=wi; wi->i=this;}
void flush() {w->flush();}
void set_minmax(LPMINMAXINFO minmax);

View File

@ -36,6 +36,10 @@ int Fl_Window::no_fullscreen_x = 0;
int Fl_Window::no_fullscreen_y = 0;
int Fl_Window::no_fullscreen_w = 0;
int Fl_Window::no_fullscreen_h = 0;
int Fl_Window::fullscreen_screen_top = -1;
int Fl_Window::fullscreen_screen_bottom = -1;
int Fl_Window::fullscreen_screen_left = -1;
int Fl_Window::fullscreen_screen_right = -1;
#endif
void Fl_Window::border(int b) {
@ -95,6 +99,23 @@ void Fl_Window::fullscreen_off() {
fullscreen_off(no_fullscreen_x, no_fullscreen_y, no_fullscreen_w, no_fullscreen_h);
}
void Fl_Window::fullscreen_screens(int top, int bottom, int left, int right) {
if ((top < 0) || (bottom < 0) || (left < 0) || (right < 0)) {
fullscreen_screen_top = -1;
fullscreen_screen_bottom = -1;
fullscreen_screen_left = -1;
fullscreen_screen_right = -1;
} else {
fullscreen_screen_top = top;
fullscreen_screen_bottom = bottom;
fullscreen_screen_left = left;
fullscreen_screen_right = right;
}
if (shown() && fullscreen_active())
fullscreen_x();
}
//
// End of "$Id$".

View File

@ -2394,9 +2394,32 @@ void Fl_X::make(Fl_Window* w)
NSRect crect;
if (w->fullscreen_active()) {
int sx, sy, sw, sh;
Fl::screen_xywh(sx, sy, sw, sh, w->x(), w->y(), w->w(), w->h());
w->resize(sx, sy, sw, sh);
int top, bottom, left, right;
int sx, sy, sw, sh, X, Y, W, H;
top = w->fullscreen_screen_top;
bottom = w->fullscreen_screen_bottom;
left = w->fullscreen_screen_left;
right = w->fullscreen_screen_right;
if ((top < 0) || (bottom < 0) || (left < 0) || (right < 0)) {
top = Fl::screen_num(w->x(), w->y(), w->w(), w->h());
bottom = top;
left = top;
right = top;
}
Fl::screen_xywh(sx, sy, sw, sh, top);
Y = sy;
Fl::screen_xywh(sx, sy, sw, sh, bottom);
H = sy + sh - Y;
Fl::screen_xywh(sx, sy, sw, sh, left);
X = sx;
Fl::screen_xywh(sx, sy, sw, sh, right);
W = sx + sw - X;
w->resize(X, Y, W, H);
winstyle = NSBorderlessWindowMask;
winlevel = NSStatusWindowLevel;
}

View File

@ -1483,7 +1483,6 @@ int Fl_X::fake_X_wm(const Fl_Window* w,int &X,int &Y, int &bt,int &bx, int &by)
Y+=yoff;
if (w->fullscreen_active()) {
X = Y = 0;
bx = by = bt = 0;
}
@ -1537,19 +1536,42 @@ void Fl_Window::resize(int X,int Y,int W,int H) {
}
}
static void make_fullscreen(Fl_Window *w, Window xid, int X, int Y, int W, int H) {
void Fl_X::make_fullscreen(int X, int Y, int W, int H) {
int top, bottom, left, right;
int sx, sy, sw, sh;
Fl::screen_xywh(sx, sy, sw, sh, X, Y, W, H);
top = w->fullscreen_screen_top;
bottom = w->fullscreen_screen_bottom;
left = w->fullscreen_screen_left;
right = w->fullscreen_screen_right;
if ((top < 0) || (bottom < 0) || (left < 0) || (right < 0)) {
top = Fl::screen_num(X, Y, W, H);
bottom = top;
left = top;
right = top;
}
Fl::screen_xywh(sx, sy, sw, sh, top);
Y = sy;
Fl::screen_xywh(sx, sy, sw, sh, bottom);
H = sy + sh - Y;
Fl::screen_xywh(sx, sy, sw, sh, left);
X = sx;
Fl::screen_xywh(sx, sy, sw, sh, right);
W = sx + sw - X;
DWORD flags = GetWindowLong(xid, GWL_STYLE);
flags = flags & ~(WS_THICKFRAME|WS_CAPTION);
SetWindowLong(xid, GWL_STYLE, flags);
// SWP_NOSENDCHANGING is so that we can override size limits
SetWindowPos(xid, HWND_TOP, sx, sy, sw, sh, SWP_NOSENDCHANGING | SWP_FRAMECHANGED);
SetWindowPos(xid, HWND_TOP, X, Y, W, H, SWP_NOSENDCHANGING | SWP_FRAMECHANGED);
}
void Fl_Window::fullscreen_x() {
_set_fullscreen();
make_fullscreen(this, fl_xid(this), x(), y(), w(), h());
i->make_fullscreen(x(), y(), w(), h());
Fl::handle(FL_FULLSCREEN, this);
}
@ -1802,8 +1824,8 @@ Fl_X* Fl_X::make(Fl_Window* w) {
monitor the window was placed on. */
RECT rect;
GetWindowRect(x->xid, &rect);
make_fullscreen(w, x->xid, rect.left, rect.top,
rect.right - rect.left, rect.bottom - rect.top);
x->make_fullscreen(rect.left, rect.top,
rect.right - rect.left, rect.bottom - rect.top);
}
x->next = Fl_X::first;

View File

@ -345,6 +345,7 @@ Atom fl_NET_WM_ICON_NAME; // utf8 aware window icon name
Atom fl_NET_SUPPORTING_WM_CHECK;
Atom fl_NET_WM_STATE;
Atom fl_NET_WM_STATE_FULLSCREEN;
Atom fl_NET_WM_FULLSCREEN_MONITORS;
Atom fl_NET_WORKAREA;
/*
@ -654,6 +655,7 @@ void fl_open_display(Display* d) {
fl_NET_SUPPORTING_WM_CHECK = XInternAtom(d, "_NET_SUPPORTING_WM_CHECK", 0);
fl_NET_WM_STATE = XInternAtom(d, "_NET_WM_STATE", 0);
fl_NET_WM_STATE_FULLSCREEN = XInternAtom(d, "_NET_WM_STATE_FULLSCREEN", 0);
fl_NET_WM_FULLSCREEN_MONITORS = XInternAtom(d, "_NET_WM_FULLSCREEN_MONITORS", 0);
fl_NET_WORKAREA = XInternAtom(d, "_NET_WORKAREA", 0);
if (sizeof(Atom) < 4)
@ -2147,22 +2149,30 @@ void Fl_Window::resize(int X,int Y,int W,int H) {
#define _NET_WM_STATE_ADD 1 /* add/set property */
#define _NET_WM_STATE_TOGGLE 2 /* toggle property */
static void send_wm_state_event(Window wnd, int add, Atom prop) {
static void send_wm_event(Window wnd, Atom message,
unsigned long d0, unsigned long d1=0,
unsigned long d2=0, unsigned long d3=0,
unsigned long d4=0) {
XEvent e;
e.xany.type = ClientMessage;
e.xany.window = wnd;
e.xclient.message_type = fl_NET_WM_STATE;
e.xclient.message_type = message;
e.xclient.format = 32;
e.xclient.data.l[0] = add ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE;
e.xclient.data.l[1] = prop;
e.xclient.data.l[2] = 0;
e.xclient.data.l[3] = 0;
e.xclient.data.l[4] = 0;
e.xclient.data.l[0] = d0;
e.xclient.data.l[1] = d1;
e.xclient.data.l[2] = d2;
e.xclient.data.l[3] = d3;
e.xclient.data.l[4] = d4;
XSendEvent(fl_display, RootWindow(fl_display, fl_screen),
0, SubstructureNotifyMask | SubstructureRedirectMask,
&e);
}
static void send_wm_state_event(Window wnd, int add, Atom prop) {
send_wm_event(wnd, fl_NET_WM_STATE,
add ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE, prop);
}
int Fl_X::ewmh_supported() {
static int result = -1;
@ -2188,6 +2198,22 @@ int Fl_X::ewmh_supported() {
/* Change an existing window to fullscreen */
void Fl_Window::fullscreen_x() {
if (Fl_X::ewmh_supported()) {
int top, bottom, left, right;
top = fullscreen_screen_top;
bottom = fullscreen_screen_bottom;
left = fullscreen_screen_left;
right = fullscreen_screen_right;
if ((top < 0) || (bottom < 0) || (left < 0) || (right < 0)) {
top = Fl::screen_num(x(), y(), w(), h());
bottom = top;
left = top;
right = top;
}
send_wm_event(fl_xid(this), fl_NET_WM_FULLSCREEN_MONITORS,
top, bottom, left, right);
send_wm_state_event(fl_xid(this), 1, fl_NET_WM_STATE_FULLSCREEN);
} else {
_set_fullscreen();
@ -2274,7 +2300,7 @@ void Fl_X::make_xid(Fl_Window* win, XVisualInfo *visual, Colormap colormap)
// force the window to be on-screen. Usually the X window manager
// does this, but a few don't, so we do it here for consistency:
int scr_x, scr_y, scr_w, scr_h;
Fl::screen_xywh(scr_x, scr_y, scr_w, scr_h, X, Y);
Fl::screen_xywh(scr_x, scr_y, scr_w, scr_h, X, Y, W, H);
if (win->border()) {
// ensure border is on screen:
@ -2303,6 +2329,23 @@ void Fl_X::make_xid(Fl_Window* win, XVisualInfo *visual, Colormap colormap)
return;
}
// Compute which screen(s) we should be on if we want to go fullscreen
int fullscreen_top, fullscreen_bottom, fullscreen_left, fullscreen_right;
fullscreen_top = win->fullscreen_screen_top;
fullscreen_bottom = win->fullscreen_screen_bottom;
fullscreen_left = win->fullscreen_screen_left;
fullscreen_right = win->fullscreen_screen_right;
if ((fullscreen_top < 0) || (fullscreen_bottom < 0) ||
(fullscreen_left < 0) || (fullscreen_right < 0)) {
fullscreen_top = Fl::screen_num(X, Y, W, H);
fullscreen_bottom = fullscreen_top;
fullscreen_left = fullscreen_top;
fullscreen_right = fullscreen_top;
}
ulong root = win->parent() ?
fl_xid(win->window()) : RootWindow(fl_display, fl_screen);
@ -2326,9 +2369,17 @@ void Fl_X::make_xid(Fl_Window* win, XVisualInfo *visual, Colormap colormap)
// border, and cannot grab without an existing window. Besides,
// there is no clear_override().
if (win->fullscreen_active() && !Fl_X::ewmh_supported()) {
int sx, sy, sw, sh;
attr.override_redirect = 1;
mask |= CWOverrideRedirect;
Fl::screen_xywh(X, Y, W, H, X, Y, W, H);
Fl::screen_xywh(sx, sy, sw, sh, fullscreen_left);
X = sx;
Fl::screen_xywh(sx, sy, sw, sh, fullscreen_right);
W = sx + sw - X;
Fl::screen_xywh(sx, sy, sw, sh, fullscreen_top);
Y = sy;
Fl::screen_xywh(sx, sy, sw, sh, fullscreen_bottom);
H = sy + sh - Y;
}
if (fl_background_pixel >= 0) {
@ -2393,6 +2444,13 @@ void Fl_X::make_xid(Fl_Window* win, XVisualInfo *visual, Colormap colormap)
// If asked for, create fullscreen
if (win->fullscreen_active() && Fl_X::ewmh_supported()) {
unsigned long data[4];
data[0] = fullscreen_top;
data[1] = fullscreen_bottom;
data[2] = fullscreen_left;
data[3] = fullscreen_right;
XChangeProperty (fl_display, xp->xid, fl_NET_WM_FULLSCREEN_MONITORS, XA_ATOM, 32,
PropModeReplace, (unsigned char*) data, 4);
XChangeProperty (fl_display, xp->xid, fl_NET_WM_STATE, XA_ATOM, 32,
PropModeAppend, (unsigned char*) &fl_NET_WM_STATE_FULLSCREEN, 1);
}

View File

@ -127,7 +127,7 @@ class fullscreen_window : public Fl_Single_Window {
fullscreen_window(int W, int H, const char *t=0);
int handle (int e);
Fl_Toggle_Light_Button *b3;
Fl_Toggle_Light_Button *b4;
};
fullscreen_window::fullscreen_window(int W, int H, const char *t) : Fl_Single_Window(W, H, t) {
@ -170,27 +170,63 @@ void border_cb(Fl_Widget *o, void *p) {
#endif
}
int px,py,pw,ph;
Fl_Button *border_button;
void fullscreen_cb(Fl_Widget *o, void *p) {
Fl_Window *w = (Fl_Window *)p;
int d = ((Fl_Button *)o)->value();
if (d) {
px = w->x();
py = w->y();
pw = w->w();
ph = w->h();
w->fullscreen();
w->override();
#ifndef WIN32 // update our border state in case border was turned off
border_button->value(w->border());
#endif
} else {
//w->fullscreen_off(px,py,pw,ph);
w->fullscreen_off();
}
}
void allscreens_cb(Fl_Widget *o, void *p) {
Fl_Window *w = (Fl_Window *)p;
int d = ((Fl_Button *)o)->value();
if (d) {
int top, bottom, left, right;
int top_y, bottom_y, left_x, right_x;
int sx, sy, sw, sh;
top = bottom = left = right = 0;
Fl::screen_xywh(sx, sy, sw, sh, 0);
top_y = sy;
bottom_y = sy + sh;
left_x = sx;
right_x = sx + sw;
for (int i = 1;i < Fl::screen_count();i++) {
Fl::screen_xywh(sx, sy, sw, sh, i);
if (sy < top_y) {
top = i;
top_y = sy;
}
if ((sy + sh) > bottom_y) {
bottom = i;
bottom_y = sy + sh;
}
if (sx < left_x) {
left = i;
left_x = sx;
}
if ((sx + sw) > right_x) {
right = i;
right_x = sx + sw;
}
}
w->fullscreen_screens(top, bottom, left, right);
} else {
w->fullscreen_screens(-1, -1, -1, -1);
}
}
void update_screeninfo(Fl_Widget *b, void *p) {
Fl_Browser *browser = (Fl_Browser *)p;
int x, y, w, h;
@ -219,7 +255,7 @@ void exit_cb(Fl_Widget *, void *) {
exit(0);
}
#define NUMB 7
#define NUMB 8
int twowindow = 0;
int initfull = 0;
@ -284,6 +320,10 @@ int main(int argc, char **argv) {
window.b3->callback(fullscreen_cb,w);
y+=30;
window.b4 = new Fl_Toggle_Light_Button(50,y,window.w()-60,30,"All Screens");
window.b4->callback(allscreens_cb,w);
y+=30;
Fl_Button eb(50,y,window.w()-60,30,"Exit");
eb.callback(exit_cb);
y+=30;