]> git.tdb.fi Git - libs/gui.git/blobdiff - source/display.cpp
Redesign and refactor
[libs/gui.git] / source / display.cpp
diff --git a/source/display.cpp b/source/display.cpp
new file mode 100644 (file)
index 0000000..724318a
--- /dev/null
@@ -0,0 +1,149 @@
+/* $Id$
+
+This file is part of libmspgbase
+Copyright © 2007 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+
+#include <iostream>
+#include <X11/Xlib.h>
+#include <X11/extensions/xf86vmode.h>
+#include <msp/core/except.h>
+#include <msp/strings/formatter.h>
+#include <msp/strings/lexicalcast.h>
+#include "display.h"
+#include "window.h"
+
+using namespace std;
+
+namespace {
+
+bool error_flag=false;
+std::string error_msg;
+
+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;
+}
+
+}
+
+namespace Msp {
+namespace Graphics {
+
+Display::Display(const string &disp_name)
+{
+       if(disp_name.empty())
+               display=XOpenDisplay(0);
+       else
+               display=XOpenDisplay(disp_name.c_str());
+       if(!display)
+               throw Exception("Couldn't open X display");
+
+       XSetErrorHandler(x_error_handler);
+
+       int nmodes;
+       XF86VidModeModeInfo **infos;
+       XF86VidModeGetAllModeLines(display, DefaultScreen(display), &nmodes, &infos);
+       for(int i=0; i<nmodes; ++i)
+       {
+               XF86VidModeModeInfo &info=*infos[i];
+       
+               VideoMode mode;
+               mode.width=info.hdisplay;
+               mode.height=info.vdisplay;
+               mode.rate=info.dotclock/(info.htotal*info.vtotal);
+               modes.push_back(mode);
+       }
+}
+
+Display::~Display()
+{
+       XCloseDisplay(display);
+       display=0;
+}
+
+void Display::add_window(Window *wnd)
+{
+       windows[wnd->get_handle()]=wnd;
+}
+
+void Display::remove_window(Window *wnd)
+{
+       windows.erase(wnd->get_handle());
+}
+
+void Display::set_mode(const VideoMode &mode)
+{
+       int screen=DefaultScreen(display);
+
+       int nmodes;
+       XF86VidModeModeInfo **infos;
+       XF86VidModeGetAllModeLines(display, screen, &nmodes, &infos);
+       for(int i=0; i<nmodes; ++i)
+       {
+               XF86VidModeModeInfo &info=*infos[i];
+
+               unsigned rate=info.dotclock/(info.htotal*info.vtotal);
+               if(info.hdisplay==mode.width && info.vdisplay==mode.height && rate==mode.rate)
+               {
+                       XF86VidModeSwitchToMode(display, screen, &info);
+                       return;
+               }
+       }
+
+       throw InvalidParameterValue("Requested mode not supported");
+}
+
+void Display::tick()
+{
+       check_error();
+
+       while(1)
+       {
+               int pending=XPending(display);
+               if(pending==0)
+                       break;
+
+               for(int i=0; i<pending; ++i)
+               {
+                       XEvent event;
+                       XNextEvent(display, &event);
+
+                       check_error();
+
+                       map<WindowHandle, Window *>::iterator j=windows.find(event.xany.window);
+                       if(j!=windows.end())
+                               j->second->event(event);
+               }
+       }
+}
+
+void Display::check_error()
+{
+       if(error_flag)
+       {
+               error_flag=false;
+               throw Exception(error_msg);
+       }
+}
+
+} // namespace Graphics
+} // namespace Msp