]> git.tdb.fi Git - libs/gui.git/blobdiff - source/gbase/display.cpp
Support ignoring keyboard autorepeat
[libs/gui.git] / source / gbase / display.cpp
index 3baf7c11a72e92eb1a1827666f247198d85e21aa..5d79ca5e9246210caa05472308e092f5b17c297b 100644 (file)
@@ -1,7 +1,7 @@
 /* $Id$
 
 This file is part of libmspgbase
-Copyright © 2007 Mikko Rasa, Mikkosoft Productions
+Copyright © 2007-2008  Mikko Rasa, Mikkosoft Productions
 Distributed under the LGPL
 */
 
@@ -15,6 +15,7 @@ Distributed under the LGPL
 #include <msp/strings/lexicalcast.h>
 #include "display.h"
 #include "window.h"
+#include "display_priv.h"
 
 using namespace std;
 
@@ -52,29 +53,51 @@ int x_error_handler(Display *display, XErrorEvent *event)
 namespace Msp {
 namespace Graphics {
 
-Display::Display(const string &disp_name)
+Display::Display(const string &disp_name):
+       priv(new Private)
 {
-#ifndef WIN32
+#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())
-               display=XOpenDisplay(0);
+               priv->display=XOpenDisplay(0);
        else
-               display=XOpenDisplay(disp_name.c_str());
-       if(!display)
+               priv->display=XOpenDisplay(disp_name.c_str());
+       if(!priv->display)
                throw Exception("Couldn't open X display");
 
        XSetErrorHandler(x_error_handler);
 
-       int screen=DefaultScreen(display);
+       int screen=DefaultScreen(priv->display);
 
        int nmodes;
        XF86VidModeModeInfo **infos;
-       XF86VidModeGetAllModeLines(display, screen, &nmodes, &infos);
+       XF86VidModeGetAllModeLines(priv->display, screen, &nmodes, &infos);
        for(int i=0; i<nmodes; ++i)
        {
                XF86VidModeModeInfo &info=*infos[i];
        
                VideoMode mode(info.hdisplay, info.vdisplay);
-               mode.rate=info.dotclock/(info.htotal*info.vtotal);
+               if(info.htotal && info.vtotal)
+                       mode.rate=info.dotclock/(info.htotal*info.vtotal);
                modes.push_back(mode);
        }
 
@@ -82,56 +105,68 @@ Display::Display(const string &disp_name)
 
        XF86VidModeModeLine modeline;
        int dotclock;
-       XF86VidModeGetModeLine(display, screen, &dotclock, &modeline);
+       XF86VidModeGetModeLine(priv->display, screen, &dotclock, &modeline);
        orig_mode=VideoMode(modeline.hdisplay, modeline.vdisplay);
-       orig_mode.rate=dotclock/(modeline.htotal*modeline.vtotal);
-#else
-       (void)disp_name;
+       if(modeline.htotal && modeline.vtotal)
+               orig_mode.rate=dotclock/(modeline.htotal*modeline.vtotal);
 #endif
 }
 
 Display::~Display()
 {
 #ifndef WIN32
-       XCloseDisplay(display);
-       display=0;
+       XCloseDisplay(priv->display);
+       delete priv;
 #endif
 }
 
-void Display::add_window(Window *wnd)
+void Display::add_window(Window &wnd)
 {
-       windows[wnd->get_handle()]=wnd;
+       priv->windows[wnd.get_private().window]=&wnd;
 }
 
-void Display::remove_window(Window *wnd)
+void Display::remove_window(Window &wnd)
 {
-       windows.erase(wnd->get_handle());
+       priv->windows.erase(wnd.get_private().window);
 }
 
 void Display::set_mode(const VideoMode &mode)
 {
-#ifndef WIN32
-       int screen=DefaultScreen(display);
+#ifdef WIN32
+       DEVMODE info;
+       info.dmSize=sizeof(DEVMODE);
+       info.dmFields=DM_PELSWIDTH|DM_PELSHEIGHT;
+       info.dmPelsWidth=mode.width;
+       info.dmPelsHeight=mode.height;
+       if(mode.rate)
+       {
+               info.dmFields|=DM_DISPLAYFREQUENCY;
+               info.dmDisplayFrequency=mode.rate;
+       }
+
+       ChangeDisplaySettings(&info, CDS_FULLSCREEN);
+#else
+       int screen=DefaultScreen(priv->display);
 
        int nmodes;
        XF86VidModeModeInfo **infos;
-       XF86VidModeGetAllModeLines(display, screen, &nmodes, &infos);
+       XF86VidModeGetAllModeLines(priv->display, screen, &nmodes, &infos);
        for(int i=0; i<nmodes; ++i)
        {
                XF86VidModeModeInfo &info=*infos[i];
 
-               unsigned rate=info.dotclock/(info.htotal*info.vtotal);
+               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(display, screen, &info);
-                       XF86VidModeSetViewPort(display, screen, 0, 0);
+                       XF86VidModeSwitchToMode(priv->display, screen, &info);
+                       XF86VidModeSetViewPort(priv->display, screen, 0, 0);
                        return;
                }
        }
 
        throw InvalidParameterValue("Requested mode not supported");
-#else
-       (void)mode;
 #endif
 }
 
@@ -148,20 +183,42 @@ void Display::tick()
                else
                        break;
 #else
-               int pending=XPending(display);
+               int pending=XPending(priv->display);
                if(pending==0)
                        break;
 
-               for(int i=0; i<pending; ++i)
+               for(; pending--;)
                {
-                       XEvent event;
-                       XNextEvent(display, &event);
+                       Window::Event event;
+                       XNextEvent(priv->display, &event.xevent);
 
                        check_error();
 
-                       map<WindowHandle, Window *>::iterator j=windows.find(event.xany.window);
-                       if(j!=windows.end())
+                       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
        }