]> git.tdb.fi Git - libs/gui.git/blobdiff - source/graphics/display.cpp
Track the currently focused window in Display
[libs/gui.git] / source / graphics / display.cpp
index da036a1baa9a4174049f40920fd0575e42fc0ceb..1c7b639a06d8f993b72ef92fb19d9d211271ed5b 100644 (file)
-#include <iostream>
-#ifndef WIN32
-#include <X11/Xlib.h>
-#ifdef WITH_XF86VIDMODE
-#include <X11/extensions/xf86vmode.h>
-#endif
-#endif
-#include <msp/strings/format.h>
-#include <msp/strings/lexicalcast.h>
+#include <cmath>
+#include <sigc++/bind.h>
 #include "display.h"
+#include "display_private.h"
 #include "window.h"
-#include "display_priv.h"
 
 using namespace std;
 
-namespace {
-
-bool error_flag = false;
-std::string error_msg;
-
-#ifndef WIN32
-int x_error_handler(Display *display, XErrorEvent *event)
-{
-       char err[128];
-       XGetErrorText(display, event->error_code, err, sizeof(err));
-
-       string request_code = Msp::lexical_cast(static_cast<int>(event->request_code));
-       char req[128];
-       XGetErrorDatabaseText(display, "XRequest", request_code.c_str(), request_code.c_str(), req, sizeof(req));
-
-       string msg = Msp::format("Request %s failed with %s [%08X]", req, err, event->resourceid);
-       if(error_flag)
-               cerr<<"Discarding error: "<<msg<<'\n';
-       else
-       {
-               cerr<<msg<<'\n';
-               error_msg = msg;
-               error_flag = true;
-       }
-
-       return 0;
-}
-#endif
-
-}
-
 namespace Msp {
 namespace Graphics {
 
-unsupported_video_mode::unsupported_video_mode(const VideoMode &mode):
-       runtime_error(format("%dx%d", mode.width, mode.height))
-{ }
-
-
-Display::Display(const string &disp_name):
-       priv(new Private)
+void Display::add_window(Window &wnd)
 {
-#ifdef WIN32
-       (void)disp_name;
-
-       for(unsigned i=0;; ++i)
-       {
-               DEVMODE info;
-               if(!EnumDisplaySettings(0, i, &info))
-                       break;
-
-               VideoMode mode(info.dmPelsWidth, info.dmPelsHeight);
-               mode.rate = info.dmDisplayFrequency;
-               modes.push_back(mode);
-       }
-       
-       DEVMODE info;
-       if(EnumDisplaySettings(0, ENUM_CURRENT_SETTINGS, &info))
-       {
-               orig_mode = VideoMode(info.dmPelsWidth, info.dmPelsHeight);
-               orig_mode.rate = info.dmDisplayFrequency;
-       }
-#else
-       if(disp_name.empty())
-               priv->display = XOpenDisplay(0);
-       else
-               priv->display = XOpenDisplay(disp_name.c_str());
-       if(!priv->display)
-               throw runtime_error("XOpenDisplay");
-
-       XSetErrorHandler(x_error_handler);
-
-#ifdef WITH_XF86VIDMODE
-       int screen = DefaultScreen(priv->display);
-
-       int nmodes;
-       XF86VidModeModeInfo **infos;
-       XF86VidModeGetAllModeLines(priv->display, screen, &nmodes, &infos);
-       for(int i=0; i<nmodes; ++i)
-       {
-               XF86VidModeModeInfo &info = *infos[i];
-       
-               VideoMode mode(info.hdisplay, info.vdisplay);
-               if(info.htotal && info.vtotal)
-                       mode.rate = info.dotclock/(info.htotal*info.vtotal);
-               modes.push_back(mode);
-       }
-
-       XFree(infos);
-
-       XF86VidModeModeLine modeline;
-       int dotclock;
-       XF86VidModeGetModeLine(priv->display, screen, &dotclock, &modeline);
-       orig_mode = VideoMode(modeline.hdisplay, modeline.vdisplay);
-       if(modeline.htotal && modeline.vtotal)
-               orig_mode.rate = dotclock/(modeline.htotal*modeline.vtotal);
-#endif
-#endif
+       priv->windows[wnd.get_private().window] = &wnd;
+       wnd.signal_got_focus.connect(sigc::bind(sigc::mem_fun(this, &Display::window_got_focus), sigc::ref(wnd)));
+       wnd.signal_lost_focus.connect(sigc::mem_fun(this, &Display::window_lost_focus));
 }
 
-Display::~Display()
+void Display::remove_window(Window &wnd)
 {
-#ifndef WIN32
-       XCloseDisplay(priv->display);
-       delete priv;
-#endif
+       priv->windows.erase(wnd.get_private().window);
+       if(&wnd==focus_window)
+               focus_window = 0;
 }
 
-void Display::add_window(Window &wnd)
+const VideoMode &Display::get_desktop_mode() const
 {
-       priv->windows[wnd.get_private().window] = &wnd;
+       if(!primary_monitor || !primary_monitor->desktop_mode)
+               throw logic_error("no desktop mode");
+       return *primary_monitor->desktop_mode;
 }
 
-void Display::remove_window(Window &wnd)
+void Display::restore_mode()
 {
-       priv->windows.erase(wnd.get_private().window);
+       for(list<Monitor>::const_iterator i=monitors.begin(); i!=monitors.end(); ++i)
+               if(i->desktop_mode)
+                       set_mode(*i->desktop_mode, false);
 }
 
-void Display::set_mode(const VideoMode &mode)
+const VideoMode *Display::find_mode(const VideoMode &mode, float rate_tolerance) const
 {
-#if defined(WIN32)
-       DEVMODE info;
-       info.dmSize = sizeof(DEVMODE);
-       info.dmFields = DM_PELSWIDTH|DM_PELSHEIGHT;
-       info.dmPelsWidth = mode.width;
-       info.dmPelsHeight = mode.height;
-       if(mode.rate)
+       const VideoMode *closest = 0;
+       float rate_diff = mode.rate;
+       for(list<VideoMode>::const_iterator i=modes.begin(); i!=modes.end(); ++i)
        {
-               info.dmFields |= DM_DISPLAYFREQUENCY;
-               info.dmDisplayFrequency = mode.rate;
+               if(mode.monitor && i->monitor!=mode.monitor)
+                       continue;
+               if(mode.rate && (i->rate<mode.rate-rate_tolerance || i->rate>mode.rate+rate_tolerance))
+                       continue;
+               if(i->width!=mode.width || i->height!=mode.height)
+                       continue;
+
+               if(mode.rate)
+               {
+                       float d = abs(i->rate-mode.rate);
+                       if(d<rate_diff)
+                       {
+                               closest = &*i;
+                               rate_diff = d;
+                       }
+               }
+               else
+                       return &*i;
        }
 
-       LONG ret = ChangeDisplaySettings(&info, CDS_FULLSCREEN);
-       if(ret!=DISP_CHANGE_SUCCESSFUL)
-               throw unsupported_video_mode(mode);
-#elif defined(WITH_XF86VIDMODE)
-       int screen = DefaultScreen(priv->display);
+       return closest;
+}
 
-       int nmodes;
-       XF86VidModeModeInfo **infos;
-       XF86VidModeGetAllModeLines(priv->display, screen, &nmodes, &infos);
-       for(int i=0; i<nmodes; ++i)
-       {
-               XF86VidModeModeInfo &info = *infos[i];
+const VideoMode *Display::find_mode(unsigned width, unsigned height) const
+{
+       return find_mode(VideoMode(width, height));
+}
 
-               unsigned rate = 0;
-               if(info.htotal && info.vtotal)
-                       rate = info.dotclock/(info.htotal*info.vtotal);
-               if(info.hdisplay==mode.width && info.vdisplay==mode.height && (mode.rate==0 || rate==mode.rate))
-               {
-                       XF86VidModeSwitchToMode(priv->display, screen, &info);
-                       XF86VidModeSetViewPort(priv->display, screen, 0, 0);
-                       return;
-               }
-       }
+void Display::window_got_focus(Window &w)
+{
+       focus_window = &w;
+}
 
-       throw unsupported_video_mode(mode);
-#else
-       (void)mode;
-       throw runtime_error("no xf86vidmode support");
-#endif
+void Display::window_lost_focus()
+{
+       focus_window = 0;
 }
 
 void Display::tick()
 {
        check_error();
 
-       while(1)
-       {
-#ifdef WIN32
-               MSG msg;
-               if(PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
-                       DispatchMessage(&msg);
-               else
-                       break;
-#else
-               int pending = XPending(priv->display);
-               if(pending==0)
-                       break;
-
-               for(; pending--;)
-               {
-                       Window::Event event;
-                       XNextEvent(priv->display, &event.xevent);
+       Window *old_focus = focus_window;
 
-                       check_error();
+       while(process_events()) ;
 
-                       map<WindowHandle, Window *>::iterator j = priv->windows.find(event.xevent.xany.window);
-                       if(j!=priv->windows.end())
-                       {
-                               /* Filter keyboard autorepeat.  If this packet is a KeyRelease and
-                               the next one is a KeyPress with the exact same parameters, they
-                               indicate autorepeat and must be dropped. */
-                               if(event.xevent.type==KeyRelease && !j->second->get_keyboard_autorepeat() && pending>0)
-                               {
-                                       XKeyEvent &kev = event.xevent.xkey;
-                                       XEvent ev2;
-                                       XPeekEvent(priv->display, &ev2);
-                                       if(ev2.type==KeyPress)
-                                       {
-                                               XKeyEvent &kev2 = ev2.xkey;
-                                               if(kev2.window==kev.window && kev2.time==kev.time && kev2.keycode==kev.keycode)
-                                               {
-                                                       XNextEvent(priv->display, &ev2);
-                                                       --pending;
-                                                       continue;
-                                               }
-                                       }
-                               }
-
-                               j->second->event(event);
-                       }
-               }
-#endif
-       }
-}
-
-void Display::check_error()
-{
-       if(error_flag)
-       {
-               error_flag = false;
-               throw runtime_error(error_msg);
-       }
+       if(old_focus && !focus_window)
+               signal_lost_focus.emit();
+       else if(!old_focus && focus_window)
+               signal_got_focus.emit();
 }
 
 } // namespace Graphics