summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorManolo Gouy <Manolo>2012-03-23 16:47:53 +0000
committerManolo Gouy <Manolo>2012-03-23 16:47:53 +0000
commit08ce2e07d379d6b9925208b5da9323f948b634db (patch)
treecff1ab07cf0952cec8c1cf874ba9bcc7b241a041
parent8cd98f5236618f8ab9d576e709308b43246bc7ac (diff)
Fix STR#2641: true fullscreen windows that cover all their screen including menu bar, task bar, dock.
git-svn-id: file:///fltk/svn/fltk/branches/branch-1.3@9299 ea41ed52-d2ee-0310-a9c1-e6b18d33e121
-rw-r--r--FL/Enumerations.H7
-rw-r--r--FL/Fl_Widget.H6
-rw-r--r--FL/Fl_Window.H22
-rw-r--r--FL/names.h1
-rw-r--r--FL/x.H1
-rw-r--r--documentation/src/enumerations.dox4
-rw-r--r--documentation/src/events.dox14
-rw-r--r--src/Fl.cxx2
-rw-r--r--src/Fl_Window.cxx6
-rw-r--r--src/Fl_Window_fullscreen.cxx64
-rw-r--r--src/Fl_cocoa.mm30
-rw-r--r--src/Fl_grab.cxx17
-rw-r--r--src/Fl_win32.cxx89
-rw-r--r--src/Fl_x.cxx148
-rw-r--r--src/screen_xywh.cxx99
-rw-r--r--test/fullscreen.cxx49
16 files changed, 460 insertions, 99 deletions
diff --git a/FL/Enumerations.H b/FL/Enumerations.H
index f0d24aca4..32825ecd0 100644
--- a/FL/Enumerations.H
+++ b/FL/Enumerations.H
@@ -3,7 +3,7 @@
//
// Enumerations for the Fast Light Tool Kit (FLTK).
//
-// Copyright 1998-2011 by Bill Spitzak and others.
+// Copyright 1998-2012 by Bill Spitzak and others.
//
// This library is free software. Distribution and use rights are outlined in
// the file "COPYING" which should have been included with this file. If this
@@ -293,7 +293,10 @@ enum Fl_Event { // events
/** The screen configuration (number, positions) was changed.
Use Fl::add_handler() to be notified of this event.
*/
- FL_SCREEN_CONFIGURATION_CHANGED = 24
+ FL_SCREEN_CONFIGURATION_CHANGED = 24,
+ /** The fullscreen state of the window has changed
+ */
+ FL_FULLSCREEN = 25
};
/** \name When Conditions */
diff --git a/FL/Fl_Widget.H b/FL/Fl_Widget.H
index 7e7ed615a..27e6801c2 100644
--- a/FL/Fl_Widget.H
+++ b/FL/Fl_Widget.H
@@ -3,7 +3,7 @@
//
// Widget header file for the Fast Light Tool Kit (FLTK).
//
-// Copyright 1998-2010 by Bill Spitzak and others.
+// Copyright 1998-2012 by Bill Spitzak and others.
//
// This library is free software. Distribution and use rights are outlined in
// the file "COPYING" which should have been included with this file. If this
@@ -170,6 +170,7 @@ protected:
NO_OVERLAY = 1<<15, ///< window not using a hardware overlay plane (Fl_Menu_Window)
GROUP_RELATIVE = 1<<16, ///< position this widget relative to the parent group, not to the window
COPIED_TOOLTIP = 1<<17, ///< the widget tooltip is internally copied, its destruction is handled by the widget
+ FULLSCREEN = 1<<18, ///< a fullscreen window (Fl_Window)
// (space for more flags)
USERFLAG3 = 1<<29, ///< reserved for 3rd party extensions
USERFLAG2 = 1<<30, ///< reserved for 3rd party extensions
@@ -843,6 +844,9 @@ public:
static unsigned int label_shortcut(const char *t);
/* Internal use only. */
static int test_shortcut(const char*, const bool require_alt = false);
+ /* Internal use only. */
+ void _set_fullscreen() {flags_ |= FULLSCREEN;}
+ void _clear_fullscreen() {flags_ &= ~FULLSCREEN;}
/** Checks if w is a child of this widget.
\param[in] w potential child widget
diff --git a/FL/Fl_Window.H b/FL/Fl_Window.H
index b6717f2e1..624e4b733 100644
--- a/FL/Fl_Window.H
+++ b/FL/Fl_Window.H
@@ -3,7 +3,7 @@
//
// Window header file for the Fast Light Tool Kit (FLTK).
//
-// Copyright 1998-2010 by Bill Spitzak and others.
+// Copyright 1998-2012 by Bill Spitzak and others.
//
// This library is free software. Distribution and use rights are outlined in
// the file "COPYING" which should have been included with this file. If this
@@ -49,6 +49,10 @@ class Fl_X;
class FL_EXPORT Fl_Window : public Fl_Group {
static char *default_xclass_;
+#if FLTK_ABI_VERSION < 10302
+ static // when these members are static, ABI compatibility with 1.3.0 is respected
+#endif
+ int no_fullscreen_x, no_fullscreen_y, no_fullscreen_w, no_fullscreen_h;
friend class Fl_X;
Fl_X *i; // points at the system-specific stuff
@@ -65,6 +69,8 @@ class FL_EXPORT Fl_Window : public Fl_Group {
Fl_Color cursor_fg, cursor_bg;
void size_range_();
void _Fl_Window(); // constructor innards
+ void fullscreen_x(); // platform-specific part of sending a window to full screen
+ void fullscreen_off_x(int X, int Y, int W, int H);// platform-specific part of leaving full screen
// unimplemented copy ctor and assignment operator
Fl_Window(const Fl_Window&);
@@ -375,15 +381,27 @@ public:
/**
Makes the window completely fill the screen, without any window
manager border visible. You must use fullscreen_off() to undo
- this. This may not work with all window managers.
+ 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.
*/
void fullscreen();
/**
+ Turns off any side effects of fullscreen()
+ */
+ void fullscreen_off();
+ /**
Turns off any side effects of fullscreen() and does
resize(x,y,w,h).
*/
void fullscreen_off(int,int,int,int);
/**
+ Returns non zero if FULLSCREEN flag is set, 0 otherwise.
+ */
+ unsigned int fullscreen_active() const { return flags() & FULLSCREEN; }
+ /**
Iconifies the window. If you call this when shown() is false
it will show() it as an icon. If the window is already
iconified this does nothing.
diff --git a/FL/names.h b/FL/names.h
index 09000c47e..5f653791e 100644
--- a/FL/names.h
+++ b/FL/names.h
@@ -67,6 +67,7 @@ const char * const fl_eventnames[] =
"FL_DND_LEAVE",
"FL_DND_RELEASE",
"FL_SCREEN_CONFIGURATION_CHANGED",
+ "FL_FULLSCREEN"
};
/**
diff --git a/FL/x.H b/FL/x.H
index 189c8050a..110f92d38 100644
--- a/FL/x.H
+++ b/FL/x.H
@@ -160,6 +160,7 @@ public:
void flush() {w->flush();}
static void x(Fl_Window* wi, int X) {wi->x(X);}
static void y(Fl_Window* wi, int Y) {wi->y(Y);}
+ static int ewmh_supported();
};
extern FL_EXPORT char fl_override_redirect; // hack into Fl_X::make_xid()
diff --git a/documentation/src/enumerations.dox b/documentation/src/enumerations.dox
index 022ce431d..47a147f12 100644
--- a/documentation/src/enumerations.dox
+++ b/documentation/src/enumerations.dox
@@ -27,7 +27,7 @@ The FLTK version number is stored in a number of compile-time constants:
\section enumerations_events Events
-Events are identified by an \p Fl_Event enumeration value. The
+Events are identified by an \ref Fl_Event enumeration value. The
following events are currently defined:
\li FL_NO_EVENT - No event (or an event fltk does not
@@ -57,6 +57,8 @@ following events are currently defined:
\li FL_DND_LEAVE - The mouse pointer left a widget still dragging
data.
\li FL_DND_RELEASE - Dragged data is about to be dropped.
+\li FL_SCREEN_CONFIGURATION_CHANGED - The screen configuration (number, positions) was changed.
+\li FL_FULLSCREEN - The fullscreen state of the window has changed.
\section enumerations_when Callback "When" Conditions
diff --git a/documentation/src/events.dox b/documentation/src/events.dox
index 2d402d2a8..3183f94eb 100644
--- a/documentation/src/events.dox
+++ b/documentation/src/events.dox
@@ -300,6 +300,20 @@ The user has released the mouse button dropping data into
the widget. If the widget returns 1, it will receive the data in
the immediately following \p FL_PASTE event.
+\section events_fl_misc Other events
+
+\subsection events_fl_screen_config FL_SCREEN_CONFIGURATION_CHANGED
+Sent whenever the screen configuration changes (a screen is added/removed,
+a screen resolution is changed, screens are moved).
+Use Fl::add_handler() to be notified of this event.
+
+\subsection events_fl_fullscreen FL_FULLSCREEN
+
+The application window has been changed from normal to fullscreen, or
+from fullscreen to normal. If you are using a X window manager which
+supports Extended Window Manager Hints, this event will not be
+delivered until the change has actually happened.
+
\section events_event_xxx Fl::event_*() methods
diff --git a/src/Fl.cxx b/src/Fl.cxx
index df77b959c..d986876f9 100644
--- a/src/Fl.cxx
+++ b/src/Fl.cxx
@@ -781,6 +781,8 @@ static handler_link *handlers = 0;
- \ref FL_SCREEN_CONFIGURATION_CHANGED events.
Under X11, this event requires the libXrandr.so shared library to be
loadable at run-time and the X server to implement the RandR extension.
+ - \ref FL_FULLSCREEN events sent to a window that enters of leaves
+ fullscreen mode.
- System events that FLTK does not recognize. See fl_xevent.
- \e Some other events when the widget FLTK selected returns
zero from its handle() method. Exactly which ones may change
diff --git a/src/Fl_Window.cxx b/src/Fl_Window.cxx
index 52bda8403..7ca25a220 100644
--- a/src/Fl_Window.cxx
+++ b/src/Fl_Window.cxx
@@ -50,6 +50,12 @@ void Fl_Window::_Fl_Window() {
resizable(0);
size_range_set = 0;
minw = maxw = minh = maxh = 0;
+#if FLTK_ABI_VERSION >= 10302
+ no_fullscreen_x = 0;
+ no_fullscreen_y = 0;
+ no_fullscreen_w = w();
+ no_fullscreen_h = h();
+#endif
callback((Fl_Callback*)default_callback);
}
diff --git a/src/Fl_Window_fullscreen.cxx b/src/Fl_Window_fullscreen.cxx
index b40986347..2cbf59788 100644
--- a/src/Fl_Window_fullscreen.cxx
+++ b/src/Fl_Window_fullscreen.cxx
@@ -31,6 +31,13 @@
#include <config.h>
+#if FLTK_ABI_VERSION < 10302
+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;
+#endif
+
void Fl_Window::border(int b) {
if (b) {
if (border()) return;
@@ -51,39 +58,44 @@ void Fl_Window::border(int b) {
#endif
}
+/* Note: The previous implementation toggled border(). With this new
+ implementation this is not necessary. Additionally, if we do that,
+ the application may lose focus when switching out of fullscreen
+ mode with some window managers. Besides, the API does not say that
+ the FLTK border state should be toggled; it only says that the
+ borders should not be *visible*.
+*/
void Fl_Window::fullscreen() {
-#ifndef WIN32
- //this would clobber the fake wm, since it relies on the border flags to
- //determine its thickness
- border(0);
-#endif
-#if defined(__APPLE__) || defined(WIN32) || defined(USE_X11)
- int sx, sy, sw, sh;
- Fl::screen_xywh(sx, sy, sw, sh, x(), y(), w(), h());
- // if we are on the main screen, we will leave the system menu bar unobstructed
- if (Fl::x()>=sx && Fl::y()>=sy && Fl::x()+Fl::w()<=sx+sw && Fl::y()+Fl::h()<=sy+sh) {
- sx = Fl::x(); sy = Fl::y();
- sw = Fl::w(); sh = Fl::h();
+ no_fullscreen_x = x();
+ no_fullscreen_y = y();
+ no_fullscreen_w = w();
+ no_fullscreen_h = h();
+ if (shown() && !(flags() & Fl_Widget::FULLSCREEN)) {
+ fullscreen_x();
+ } else {
+ set_flag(FULLSCREEN);
}
- if (x()==sx) x(sx+1); // make sure that we actually execute the resize
-#if defined(USE_X11)
- resize(0, 0, w(), h()); // work around some quirks in X11
-#endif
- resize(sx, sy, sw, sh);
-#else
- if (!x()) x(1); // make sure that we actually execute the resize
- resize(0,0,Fl::w(),Fl::h());
-#endif
}
void Fl_Window::fullscreen_off(int X,int Y,int W,int H) {
- // this order produces less blinking on IRIX:
- resize(X,Y,W,H);
-#ifndef WIN32
- border(1);
-#endif
+ if (shown() && (flags() & Fl_Widget::FULLSCREEN)) {
+ fullscreen_off_x(X, Y, W, H);
+ } else {
+ clear_flag(FULLSCREEN);
+ }
+ no_fullscreen_x = no_fullscreen_y = no_fullscreen_w = no_fullscreen_h = 0;
}
+void Fl_Window::fullscreen_off() {
+ if (!no_fullscreen_x && !no_fullscreen_y) {
+ // Window was initially created fullscreen - default to current monitor
+ no_fullscreen_x = x();
+ no_fullscreen_y = y();
+ }
+ fullscreen_off(no_fullscreen_x, no_fullscreen_y, no_fullscreen_w, no_fullscreen_h);
+}
+
+
//
// End of "$Id$".
//
diff --git a/src/Fl_cocoa.mm b/src/Fl_cocoa.mm
index 693cd07a4..8c1a5474b 100644
--- a/src/Fl_cocoa.mm
+++ b/src/Fl_cocoa.mm
@@ -931,6 +931,10 @@ void fl_open_callback(void (*cb)(const char *)) {
fl_lock_function();
FLWindow *nsw = (FLWindow*)[notif object];
Fl_Window *window = [nsw getFl_Window];
+ /* Fullscreen windows obscure all other windows so we need to return
+ to a "normal" level when the user switches to another window */
+ if (window->fullscreen_active())
+ [nsw setLevel:NSNormalWindowLevel];
Fl::handle( FL_UNFOCUS, window);
fl_unlock_function();
}
@@ -939,6 +943,9 @@ void fl_open_callback(void (*cb)(const char *)) {
fl_lock_function();
FLWindow *nsw = (FLWindow*)[notif object];
Fl_Window *w = [nsw getFl_Window];
+ /* Restore previous fullscreen level */
+ if (w->fullscreen_active())
+ [nsw setLevel:NSStatusWindowLevel];
if ( w->border() || (!w->modal() && !w->tooltip_window()) ) Fl::handle( FL_FOCUS, w);
fl_unlock_function();
}
@@ -1914,6 +1921,22 @@ static void q_set_window_title(NSWindow *nsw, const char * name, const char *mi
@end
+void Fl_Window::fullscreen_x() {
+ _set_fullscreen();
+ /* On OS X < 10.6, it is necessary to recreate the window. This is done
+ with hide+show. */
+ hide();
+ show();
+ Fl::handle(FL_FULLSCREEN, this);
+}
+
+void Fl_Window::fullscreen_off_x(int X, int Y, int W, int H) {
+ _clear_fullscreen();
+ hide();
+ resize(X, Y, W, H);
+ show();
+ Fl::handle(FL_FULLSCREEN, this);
+}
/*
* go ahead, create that (sub)window
@@ -2030,6 +2053,13 @@ void Fl_X::make(Fl_Window* w)
x->gc = 0;
NSRect crect;
+ if (w->flags() & Fl_Widget::FULLSCREEN) {
+ 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);
+ winstyle = NSBorderlessWindowMask;
+ winlevel = NSStatusWindowLevel;
+ }
crect.origin.x = w->x();
crect.origin.y = main_screen_height - (w->y() + w->h());
crect.size.width=w->w();
diff --git a/src/Fl_grab.cxx b/src/Fl_grab.cxx
index e7951ebc1..c8f7ac941 100644
--- a/src/Fl_grab.cxx
+++ b/src/Fl_grab.cxx
@@ -42,6 +42,15 @@ extern void *fl_capture;
#endif
void Fl::grab(Fl_Window* win) {
+#if USE_X11
+ Fl_Window *fullscreen_win = NULL;
+ for (Fl_Window *W = Fl::first_window(); W; W = Fl::next_window(W)) {
+ if (W->fullscreen_active()) {
+ fullscreen_win = W;
+ break;
+ }
+ }
+#endif
if (win) {
if (!grab_) {
#ifdef WIN32
@@ -51,8 +60,9 @@ void Fl::grab(Fl_Window* win) {
fl_capture = Fl_X::i(first_window())->xid;
Fl_X::i(first_window())->set_key_window();
#else
+ Window xid = fullscreen_win ? fl_xid(fullscreen_win) : fl_xid(first_window());
XGrabPointer(fl_display,
- fl_xid(first_window()),
+ xid,
1,
ButtonPressMask|ButtonReleaseMask|
ButtonMotionMask|PointerMotionMask,
@@ -62,7 +72,7 @@ void Fl::grab(Fl_Window* win) {
0,
fl_event_time);
XGrabKeyboard(fl_display,
- fl_xid(first_window()),
+ xid,
1,
GrabModeAsync,
GrabModeAsync,
@@ -78,7 +88,10 @@ void Fl::grab(Fl_Window* win) {
#elif defined(__APPLE__)
fl_capture = 0;
#else
+ // We must keep the grab in the non-EWMH fullscreen case
+ if (!fullscreen_win || Fl_X::ewmh_supported()) {
XUngrabKeyboard(fl_display, fl_event_time);
+ }
XUngrabPointer(fl_display, fl_event_time);
// this flush is done in case the picked menu item goes into
// an infinite loop, so we don't leave the X server locked up:
diff --git a/src/Fl_win32.cxx b/src/Fl_win32.cxx
index 531a7ab76..5530e76f0 100644
--- a/src/Fl_win32.cxx
+++ b/src/Fl_win32.cxx
@@ -1315,6 +1315,11 @@ int Fl_X::fake_X_wm(const Fl_Window* w,int &X,int &Y, int &bt,int &bx, int &by)
X+=xoff;
Y+=yoff;
+ if (w->flags() & Fl_Widget::FULLSCREEN) {
+ X = Y = 0;
+ bx = by = bt = 0;
+ }
+
return ret;
}
@@ -1365,6 +1370,58 @@ 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) {
+ int sx, sy, sw, sh;
+ Fl::screen_xywh(sx, sy, sw, sh, X, Y, W, H);
+ 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);
+}
+
+void Fl_Window::fullscreen_x() {
+ _set_fullscreen();
+ make_fullscreen(this, fl_xid(this), x(), y(), w(), h());
+ Fl::handle(FL_FULLSCREEN, this);
+}
+
+void Fl_Window::fullscreen_off_x(int X, int Y, int W, int H) {
+ _clear_fullscreen();
+ DWORD style = GetWindowLong(fl_xid(this), GWL_STYLE);
+ // Remove the xid temporarily so that Fl_X::fake_X_wm() behaves like it
+ // does in Fl_X::make().
+ HWND xid = fl_xid(this);
+ Fl_X::i(this)->xid = NULL;
+ int wx, wy, bt, bx, by;
+ switch (Fl_X::fake_X_wm(this, wx, wy, bt, bx, by)) {
+ case 0:
+ break;
+ case 1:
+ style |= WS_CAPTION;
+ break;
+ case 2:
+ if (border()) {
+ style |= WS_THICKFRAME | WS_CAPTION;
+ }
+ break;
+ }
+ Fl_X::i(this)->xid = xid;
+ // Adjust for decorations (but not if that puts the decorations
+ // outside the screen)
+ if ((X != x()) || (Y != y())) {
+ X -= bx;
+ Y -= by+bt;
+ }
+ W += bx*2;
+ H += by*2+bt;
+ SetWindowLong(fl_xid(this), GWL_STYLE, style);
+ SetWindowPos(fl_xid(this), 0, X, Y, W, H,
+ SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED);
+ Fl::handle(FL_FULLSCREEN, this);
+}
+
+
////////////////////////////////////////////////////////////////
/*
@@ -1494,18 +1551,26 @@ Fl_X* Fl_X::make(Fl_Window* w) {
int xwm = xp , ywm = yp , bt, bx, by;
switch (fake_X_wm(w, xwm, ywm, bt, bx, by)) {
// No border (used for menus)
- case 0: style |= WS_POPUP;
- styleEx |= WS_EX_TOOLWINDOW;
+ case 0:
+ style |= WS_POPUP;
+ styleEx |= WS_EX_TOOLWINDOW;
break;
// Thin border and title bar
- case 1: style |= WS_DLGFRAME | WS_CAPTION; break;
+ case 1:
+ style |= WS_DLGFRAME | WS_CAPTION;
+ if (!w->modal())
+ style |= WS_SYSMENU | WS_MINIMIZEBOX;
+ break;
// Thick, resizable border and title bar, with maximize button
- case 2: style |= WS_THICKFRAME | WS_MAXIMIZEBOX | WS_CAPTION ; break;
+ case 2:
+ style |= WS_THICKFRAME | WS_SYSMENU | WS_MAXIMIZEBOX | WS_CAPTION;
+ if (!w->modal())
+ style |= WS_MINIMIZEBOX;
+ break;
}
if (by+bt) {
- if (!w->modal()) style |= WS_SYSMENU | WS_MINIMIZEBOX;
wp += 2*bx;
hp += 2*by+bt;
}
@@ -1561,6 +1626,18 @@ Fl_X* Fl_X::make(Fl_Window* w) {
);
if (lab) free(lab);
+ if (w->flags() & Fl_Widget::FULLSCREEN) {
+ /* We need to make sure that the fullscreen is created on the
+ default monitor, ie the desktop where the shortcut is located
+ etc. This requires that CreateWindow is called with CW_USEDEFAULT
+ for x and y. We can then use GetWindowRect to determine which
+ 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->next = Fl_X::first;
Fl_X::first = x;
@@ -1576,7 +1653,7 @@ Fl_X* Fl_X::make(Fl_Window* w) {
// If we've captured the mouse, we dont want to activate any
// other windows from the code, or we lose the capture.
ShowWindow(x->xid, !showit ? SW_SHOWMINNOACTIVE :
- (Fl::grab() || (style & WS_POPUP)) ? SW_SHOWNOACTIVATE : SW_SHOWNORMAL);
+ (Fl::grab() || (styleEx & WS_EX_TOOLWINDOW)) ? SW_SHOWNOACTIVATE : SW_SHOWNORMAL);
// Register all windows for potential drag'n'drop operations
fl_OleInitialize();
diff --git a/src/Fl_x.cxx b/src/Fl_x.cxx
index 3ee226173..330d363ec 100644
--- a/src/Fl_x.cxx
+++ b/src/Fl_x.cxx
@@ -325,6 +325,9 @@ Atom fl_XaUtf8String;
Atom fl_XaTextUriList;
Atom fl_NET_WM_NAME; // utf8 aware window label
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;
/*
X defines 32-bit-entities to have a format value of max. 32,
@@ -623,6 +626,9 @@ void fl_open_display(Display* d) {
fl_XaTextUriList = XInternAtom(d, "text/uri-list", 0);
fl_NET_WM_NAME = XInternAtom(d, "_NET_WM_NAME", 0);
fl_NET_WM_ICON_NAME = XInternAtom(d, "_NET_WM_ICON_NAME", 0);
+ 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);
if (sizeof(Atom) < 4)
atom_bits = sizeof(Atom) * 8;
@@ -784,6 +790,31 @@ void fl_sendClientMessage(Window window, Atom message,
XSendEvent(fl_display, window, 0, 0, &e);
}
+
+/*
+ Get window property value (32 bit format)
+ Returns zero on success, -1 on error
+*/
+static int get_xwinprop(Window wnd, Atom prop, long max_length,
+ unsigned long *nitems, unsigned long **data) {
+ Atom actual;
+ int format;
+ unsigned long bytes_after;
+
+ if (Success != XGetWindowProperty(fl_display, wnd, prop, 0, max_length,
+ False, AnyPropertyType, &actual, &format,
+ nitems, &bytes_after, (unsigned char**)data)) {
+ return -1;
+ }
+
+ if (actual == None || format != 32) {
+ return -1;
+ }
+
+ return 0;
+}
+
+
////////////////////////////////////////////////////////////////
// Code for copying to clipboard and DnD out of the program:
@@ -1480,6 +1511,31 @@ int fl_handle(const XEvent& thisevent)
in_a_window = true;
break;
+ case PropertyNotify:
+ if (xevent.xproperty.atom == fl_NET_WM_STATE) {
+ int fullscreen_state = 0;
+ if (xevent.xproperty.state != PropertyDelete) {
+ unsigned long nitems;
+ unsigned long *words = 0;
+ if (0 == get_xwinprop(xid, fl_NET_WM_STATE, 64, &nitems, &words) ) {
+ for (unsigned long item = 0; item < nitems; item++) {
+ if (words[item] == fl_NET_WM_STATE_FULLSCREEN) {
+ fullscreen_state = 1;
+ }
+ }
+ }
+ }
+ if (window->fullscreen_active() && !fullscreen_state) {
+ window->_clear_fullscreen();
+ event = FL_FULLSCREEN;
+ }
+ if (!window->fullscreen_active() && fullscreen_state) {
+ window->_set_fullscreen();
+ event = FL_FULLSCREEN;
+ }
+ }
+ break;
+
case MotionNotify:
set_event_xy();
# if CONSOLIDATE_MOTION
@@ -1619,6 +1675,75 @@ void Fl_Window::resize(int X,int Y,int W,int H) {
////////////////////////////////////////////////////////////////
+#define _NET_WM_STATE_REMOVE 0 /* remove/unset property */
+#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) {
+ XEvent e;
+ e.xany.type = ClientMessage;
+ e.xany.window = wnd;
+ e.xclient.message_type = fl_NET_WM_STATE;
+ 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;
+ XSendEvent(fl_display, RootWindow(fl_display, fl_screen),
+ 0, SubstructureNotifyMask | SubstructureRedirectMask,
+ &e);
+}
+
+int Fl_X::ewmh_supported() {
+ static int result = -1;
+
+ if (result == -1) {
+ result = 0;
+ unsigned long nitems;
+ unsigned long *words = 0;
+ if (0 == get_xwinprop(XRootWindow(fl_display, fl_screen), fl_NET_SUPPORTING_WM_CHECK, 64,
+ &nitems, &words) && nitems == 1) {
+ Window child = words[0];
+ if (0 == get_xwinprop(child, fl_NET_SUPPORTING_WM_CHECK, 64,
+ &nitems, &words) && nitems == 1) {
+ result = (child == words[0]);
+ }
+ }
+ }
+
+ return result;
+}
+
+/* Change an existing window to fullscreen */
+void Fl_Window::fullscreen_x() {
+ if (Fl_X::ewmh_supported()) {
+ send_wm_state_event(fl_xid(this), 1, fl_NET_WM_STATE_FULLSCREEN);
+ } else {
+ _set_fullscreen();
+ hide();
+ show();
+ /* We want to grab the window, not a widget, so we cannot use Fl::grab */
+ XGrabKeyboard(fl_display, fl_xid(this), 1, GrabModeAsync, GrabModeAsync, fl_event_time);
+ Fl::handle(FL_FULLSCREEN, this);
+ }
+}
+
+void Fl_Window::fullscreen_off_x(int X, int Y, int W, int H) {
+ if (Fl_X::ewmh_supported()) {
+ send_wm_state_event(fl_xid(this), 0, fl_NET_WM_STATE_FULLSCREEN);
+ } else {
+ _clear_fullscreen();
+ /* The grab will be lost when the window is destroyed */
+ hide();
+ resize(X,Y,W,H);
+ show();
+ Fl::handle(FL_FULLSCREEN, this);
+ }
+}
+
+////////////////////////////////////////////////////////////////
+
// A subclass of Fl_Window may call this to associate an X window it
// creates with the Fl_Window:
@@ -1654,6 +1779,7 @@ ExposureMask|StructureNotifyMask
|KeyPressMask|KeyReleaseMask|KeymapStateMask|FocusChangeMask
|ButtonPressMask|ButtonReleaseMask
|EnterWindowMask|LeaveWindowMask
+|PropertyChangeMask
|PointerMotionMask;
void Fl_X::make_xid(Fl_Window* win, XVisualInfo *visual, Colormap colormap)
@@ -1725,6 +1851,16 @@ void Fl_X::make_xid(Fl_Window* win, XVisualInfo *visual, Colormap colormap)
attr.save_under = 1; mask |= CWSaveUnder;
if (!win->border()) {attr.override_redirect = 1; mask |= CWOverrideRedirect;}
}
+ // For the non-EWMH fullscreen case, we cannot use the code above,
+ // since we do not want save_under, do not want to turn off the
+ // border, and cannot grab without an existing window. Besides,
+ // there is no clear_override().
+ if (win->flags() & Fl_Widget::FULLSCREEN && !Fl_X::ewmh_supported()) {
+ attr.override_redirect = 1;
+ mask |= CWOverrideRedirect;
+ Fl::screen_xywh(X, Y, W, H, X, Y, W, H);
+ }
+
if (fl_background_pixel >= 0) {
attr.background_pixel = fl_background_pixel;
fl_background_pixel = -1;
@@ -1785,6 +1921,12 @@ void Fl_X::make_xid(Fl_Window* win, XVisualInfo *visual, Colormap colormap)
PropModeAppend, (unsigned char*) &net_wm_state_skip_taskbar, 1);
}
+ // If asked for, create fullscreen
+ if (win->flags() & Fl_Widget::FULLSCREEN && Fl_X::ewmh_supported()) {
+ XChangeProperty (fl_display, xp->xid, fl_NET_WM_STATE, XA_ATOM, 32,
+ PropModeAppend, (unsigned char*) &fl_NET_WM_STATE_FULLSCREEN, 1);
+ }
+
// Make it receptive to DnD:
long version = 4;
XChangeProperty(fl_display, xp->xid, fl_XdndAware,
@@ -1822,6 +1964,12 @@ void Fl_X::make_xid(Fl_Window* win, XVisualInfo *visual, Colormap colormap)
Fl::e_number = old_event;
win->redraw();
}
+
+ // non-EWMH fullscreen case, need grab
+ if (win->flags() & Fl_Widget::FULLSCREEN && !Fl_X::ewmh_supported()) {
+ XGrabKeyboard(fl_display, xp->xid, 1, GrabModeAsync, GrabModeAsync, fl_event_time);
+ }
+
}
////////////////////////////////////////////////////////////////
diff --git a/src/screen_xywh.cxx b/src/screen_xywh.cxx
index aa7719eb0..2a202ada3 100644
--- a/src/screen_xywh.cxx
+++ b/src/screen_xywh.cxx
@@ -21,6 +21,7 @@
#include <FL/x.H>
#include <config.h>
+#define MAX_SCREENS 16
// Number of screens returned by multi monitor aware API; -1 before init
static int num_screens = -1;
@@ -142,47 +143,61 @@ static void screen_init() {
num_screens = count;
}
-#elif HAVE_XINERAMA
-# include <X11/extensions/Xinerama.h>
+#else
-// Screen data...
-static XineramaScreenInfo *screens;
-static float dpi[16][2];
+#if HAVE_XINERAMA
+# include <X11/extensions/Xinerama.h>
+#endif
+typedef struct {
+ short x_org;
+ short y_org;
+ short width;
+ short height;
+ } FLScreenInfo;
+static FLScreenInfo screens[MAX_SCREENS];
+static float dpi[MAX_SCREENS][2];
static void screen_init() {
if (!fl_display) fl_open_display();
-
+ // FIXME: Rewrite using RandR instead
+#if HAVE_XINERAMA
if (XineramaIsActive(fl_display)) {
- screens = XineramaQueryScreens(fl_display, &num_screens);
- int i;
- // Xlib and Xinerama may disagree on the screen count. Sigh...
- // Use the minimum of the reported counts.
- // Use the previous screen's info for non-existent ones.
- int sc = ScreenCount(fl_display); // Xlib screen count
- for (i=0; i<num_screens; i++) {
- int mm = (i < sc) ? DisplayWidthMM(fl_display, i) : 0;
- dpi[i][0] = mm ? screens[i].width*25.4f/mm : (i > 0) ? dpi[i-1][0] : 0.0f;
- mm = (i < sc) ? DisplayHeightMM(fl_display, i) : 0;
- dpi[i][1] = mm ? screens[i].height*25.4f/mm : (i > 0) ? dpi[i-1][1] : 0.0f;
+ XineramaScreenInfo *xsi = XineramaQueryScreens(fl_display, &num_screens);
+ if (num_screens > MAX_SCREENS) num_screens = MAX_SCREENS;
+
+ /* There's no way to use different DPI for different Xinerama screens. */
+ for (int i=0; i<num_screens; i++) {
+ screens[i].x_org = xsi[i].x_org;
+ screens[i].y_org = xsi[i].y_org;
+ screens[i].width = xsi[i].width;
+ screens[i].height = xsi[i].height;
+
+ int mm = DisplayWidthMM(fl_display, fl_screen);
+ dpi[i][0] = mm ? screens[i].width*25.4f/mm : 0.0f;
+ mm = DisplayHeightMM(fl_display, fl_screen);
+ dpi[i][1] = mm ? screens[i].height*25.4f/mm : 0.0f;
+ }
+ if (xsi) XFree(xsi);
+ } else
+#endif
+ { // ! XineramaIsActive()
+ num_screens = ScreenCount(fl_display);
+ if (num_screens > MAX_SCREENS) num_screens = MAX_SCREENS;
+
+ for (int i=0; i<num_screens; i++) {
+ screens[i].x_org = 0;
+ screens[i].y_org = 0;
+ screens[i].width = DisplayWidth(fl_display, i);
+ screens[i].height = DisplayHeight(fl_display, i);
+
+ int mm = DisplayWidthMM(fl_display, i);
+ dpi[i][0] = mm ? DisplayWidth(fl_display, i)*25.4f/mm : 0.0f;
+ mm = DisplayHeightMM(fl_display, i);
+ dpi[i][1] = mm ? DisplayHeight(fl_display, i)*25.4f/mm : 0.0f;
}
- } else { // ! XineramaIsActive()
- num_screens = 1;
- int mm = DisplayWidthMM(fl_display, fl_screen);
- dpi[0][0] = mm ? Fl::w()*25.4f/mm : 0.0f;
- mm = DisplayHeightMM(fl_display, fl_screen);
- dpi[0][1] = mm ? Fl::h()*25.4f/mm : dpi[0][0];
}
}
-#else
-static float dpi[2];
-static void screen_init() {
- num_screens = 1;
- if (!fl_display) fl_open_display();
- int mm = DisplayWidthMM(fl_display, fl_screen);
- dpi[0] = mm ? Fl::w()*25.4f/mm : 0.0f;
- mm = DisplayHeightMM(fl_display, fl_screen);
- dpi[1] = mm ? Fl::h()*25.4f/mm : dpi[0];
-}
+
#endif // WIN32
#ifndef FL_DOXYGEN
@@ -297,20 +312,11 @@ void Fl::screen_xywh(int &X, int &Y, int &W, int &H, int n) {
W = screens[n].width;
H = screens[n].height;
#else
-#if HAVE_XINERAMA
- if (num_screens > 0 && screens) {
+ if (num_screens > 0) {
X = screens[n].x_org;
Y = screens[n].y_org;
W = screens[n].width;
H = screens[n].height;
- } else
-#endif // HAVE_XINERAMA
- {
- /* Fallback if something is broken (or no Xinerama)... */
- X = 0;
- Y = 0;
- W = DisplayWidth(fl_display, fl_screen);
- H = DisplayHeight(fl_display, fl_screen);
}
#endif // WIN32
}
@@ -372,16 +378,11 @@ void Fl::screen_dpi(float &h, float &v, int n)
h = dpi_h[n];
v = dpi_v[n];
}
-#elif HAVE_XINERAMA
+#else
if (n >= 0 && n < num_screens) {
h = dpi[n][0];
v = dpi[n][1];
}
-#else
- if (n >= 0 && n < num_screens) {
- h = dpi[0];
- v = dpi[1];
- }
#endif // WIN32
}
diff --git a/test/fullscreen.cxx b/test/fullscreen.cxx
index 5ecb6ea74..ff598bfa9 100644
--- a/test/fullscreen.cxx
+++ b/test/fullscreen.cxx
@@ -54,8 +54,11 @@
#include <FL/Fl.H>
#include <FL/Fl_Single_Window.H>
#include <FL/Fl_Hor_Slider.H>
+#include <FL/Fl_Input.H>
+#include <FL/Fl_Menu_Button.H>
#include <FL/Fl_Toggle_Light_Button.H>
#include <FL/math.h>
+#include <FL/fl_ask.H>
#include <stdio.h>
#if HAVE_GL
@@ -118,6 +121,27 @@ void shape_window::draw() {
#endif
+class fullscreen_window : public Fl_Single_Window {
+ public:
+ fullscreen_window(int W, int H, const char *t=0);
+ int handle (int e);
+ Fl_Toggle_Light_Button *b3;
+
+};
+
+fullscreen_window::fullscreen_window(int W, int H, const char *t) : Fl_Single_Window(W, H, t) {
+
+}
+
+int fullscreen_window::handle(int e) {
+ if (e == FL_FULLSCREEN) {
+ printf("Received FL_FULLSCREEN event\n");
+ b3->value(fullscreen_active());
+ }
+ if (Fl_Single_Window::handle(e)) return 1;
+ return 0;
+}
+
void sides_cb(Fl_Widget *o, void *p) {
shape_window *sw = (shape_window *)p;
sw->sides = int(((Fl_Slider *)o)->value());
@@ -155,13 +179,14 @@ void fullscreen_cb(Fl_Widget *o, void *p) {
py = w->y();
pw = w->w();
ph = w->h();
-#ifndef WIN32//necessary because fullscreen removes border
- border_button->value(0);
- border_button->do_callback();
-#endif
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(px,py,pw,ph);
+ w->fullscreen_off();
}
}
@@ -171,7 +196,7 @@ void exit_cb(Fl_Widget *, void *) {
exit(0);
}
-#define NUMB 5
+#define NUMB 6
int twowindow = 0;
int initfull = 0;
@@ -187,9 +212,10 @@ int main(int argc, char **argv) {
if (Fl::args(argc,argv,i,arg) < argc)
Fl::fatal("Options are:\n -2 = 2 windows\n -f = startup fullscreen\n%s",Fl::help);
- Fl_Single_Window window(300,300+30*NUMB); window.end();
+ fullscreen_window window(300,300+30*NUMB); window.end();
shape_window sw(10,10,window.w()-20,window.h()-30*NUMB-20);
+
#if HAVE_GL
sw.mode(FL_RGB);
#endif
@@ -222,21 +248,24 @@ int main(int argc, char **argv) {
b1.callback(double_cb,&sw);
y+=30;
+ Fl_Input i1(50,y,window.w()-60,30, "Input");
+ y+=30;
+
Fl_Toggle_Light_Button b2(50,y,window.w()-60,30,"Border");
b2.callback(border_cb,w);
b2.set();
border_button = &b2;
y+=30;
- Fl_Toggle_Light_Button b3(50,y,window.w()-60,30,"FullScreen");
- b3.callback(fullscreen_cb,w);
+ window.b3 = new Fl_Toggle_Light_Button(50,y,window.w()-60,30,"FullScreen");
+ window.b3->callback(fullscreen_cb,w);
y+=30;
Fl_Button eb(50,y,window.w()-60,30,"Exit");
eb.callback(exit_cb);
y+=30;
- if (initfull) {b3.set(); b3.do_callback();}
+ if (initfull) {window.b3->set(); window.b3->do_callback();}
window.end();
window.show(argc,argv);