diff options
| author | Manolo Gouy <Manolo> | 2012-03-23 16:47:53 +0000 |
|---|---|---|
| committer | Manolo Gouy <Manolo> | 2012-03-23 16:47:53 +0000 |
| commit | 08ce2e07d379d6b9925208b5da9323f948b634db (patch) | |
| tree | cff1ab07cf0952cec8c1cf874ba9bcc7b241a041 | |
| parent | 8cd98f5236618f8ab9d576e709308b43246bc7ac (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.H | 7 | ||||
| -rw-r--r-- | FL/Fl_Widget.H | 6 | ||||
| -rw-r--r-- | FL/Fl_Window.H | 22 | ||||
| -rw-r--r-- | FL/names.h | 1 | ||||
| -rw-r--r-- | FL/x.H | 1 | ||||
| -rw-r--r-- | documentation/src/enumerations.dox | 4 | ||||
| -rw-r--r-- | documentation/src/events.dox | 14 | ||||
| -rw-r--r-- | src/Fl.cxx | 2 | ||||
| -rw-r--r-- | src/Fl_Window.cxx | 6 | ||||
| -rw-r--r-- | src/Fl_Window_fullscreen.cxx | 64 | ||||
| -rw-r--r-- | src/Fl_cocoa.mm | 30 | ||||
| -rw-r--r-- | src/Fl_grab.cxx | 17 | ||||
| -rw-r--r-- | src/Fl_win32.cxx | 89 | ||||
| -rw-r--r-- | src/Fl_x.cxx | 148 | ||||
| -rw-r--r-- | src/screen_xywh.cxx | 99 | ||||
| -rw-r--r-- | test/fullscreen.cxx | 49 |
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" }; /** @@ -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); |
