-#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