{-# LANGUAGE ScopedTypeVariables #-}
-- -*-haskell-*-

#include <gtk/gtk.h>
#include "template-hsc-gtk2hs.h"

--  GIMP Toolkit (GTK) GDK Events
--
--  Author : Axel Simon
--
--  Created: 27 April 2001
--
--  Copyright (C) 2001-2005 Axel Simon
--
--  This library is free software; you can redistribute it and/or
--  modify it under the terms of the GNU Lesser General Public
--  License as published by the Free Software Foundation; either
--  version 2.1 of the License, or (at your option) any later version.
--
--  This library is distributed in the hope that it will be useful,
--  but WITHOUT ANY WARRANTY; without even the implied warranty of
--  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
--  Lesser General Public License for more details.
--
-- |
-- Maintainer  : gtk2hs-users\@lists.sourceforge.net
-- Stability   : deprecated
-- Portability : portable (depends on GHC)
--
-- Definition of a record that contains event information. Deprecated in
-- favor of 'Graphics.UI.Gtk.Gdk.EventM' and not exported by Gtk.hs.
--
module Graphics.UI.Gtk.Gdk.Events (
  Modifier(..),         -- a mask of control keys
  TimeStamp,
  currentTime,

  -- | Deprecated way of conveying event information.
  Event(..),            -- information in event callbacks from Gdk
  EventButton,
  EventScroll,
  EventMotion,
  EventExpose,
  EventKey,
  EventConfigure,
  EventCrossing,
  EventFocus,
  EventProperty,
  EventProximity,
  EventVisibility,
  EventWindowState,
  EventGrabBroken,

  marshExposeRect,

  -- selector functions
  marshalEvent,         -- convert a pointer to an event data structure
  -- used data structures
  VisibilityState(..),
  CrossingMode(..),
  NotifyType(..),
  WindowState(..),
  ScrollDirection(..),
  MouseButton(..),
  Click(..),
  Rectangle(..)
  ) where

import System.IO.Unsafe (unsafeInterleaveIO)
import System.Glib.FFI
import System.Glib.UTFString
import System.Glib.Flags
import Graphics.UI.Gtk.Gdk.Keys         (KeyVal, keyvalToChar, keyvalName)
#if GTK_MAJOR_VERSION < 3
import Graphics.UI.Gtk.Gdk.Region       (Region, makeNewRegion)
#endif
import Graphics.UI.Gtk.Gdk.Enums        (Modifier(..),
                                         VisibilityState(..),
                                         CrossingMode(..),
                                         NotifyType(..),
                                         WindowState(..),
                                         ScrollDirection(..))
import Graphics.UI.Gtk.General.Enums    (MouseButton(..), Click(..))
import Graphics.UI.Gtk.General.Structs  (Rectangle(..))

-- | The time (in milliseconds) when an event happened. This is used mostly
-- for ordering events and responses to events.
--
type TimeStamp = Word32
-- TODO: make this a newtype

-- | Represents the current time, and can be used anywhere a time is expected.
currentTime :: TimeStamp
currentTime = #{const GDK_CURRENT_TIME}

-- Note on Event:
-- * 'Event' can communicate a small array of data to another widget. This
--   functionality is not bound as it can be done easier in Haskell.
--
-- * EventDND is not implemented as registering a DND source or sink
--   should be easier and sufficient for everything.
--
-- * EventProperty is not bound since it involves Atoms and its hard to see
--   how a Haskell application should extract the data. It should be possible
--   to connect directly to 'propertyChanged' signals. If there is a need
--   to monitor a property for which there is no signal we could add
--   a trigger for just that property.
--
-- * EventSelection - I don\'t quite see how this works, so not bound.
--
-- * NoExpose - seems pointless: you copy from a drawable and this signal
--   tells you that it was up-to-date without redrawing. Maybe I'm missing
--   something.
--
-- * EventSetting informs about a change in setting that are shared among
--   several applications. They are probably not relevant to user defined
--   widgets. Anyway they don\'t make sense before GtkSettings isn\'t bound.
--
-- * Property is a TODO. These come from RC files which are useful for
--   custom widgets.

-- | An event that contains information on a button press.
type EventButton = Event

-- | An event that contains information on scrolling.
type EventScroll = Event

-- | An event that contains information on the movement of the mouse pointer.
type EventMotion = Event

-- | An area of the 'DrawWindow' needs redrawing.
type EventExpose = Event

-- | An event that contains information about a key press.
type EventKey = Event

-- | An event that contains the new size of a window.
type EventConfigure = Event

-- | Generated when the pointer enters or leaves a window.
type EventCrossing = Event

-- | An event that informs about a change of the input focus.
type EventFocus = Event

-- | An event that indicates a property of the window changed.
type EventProperty = Event

-- | An event that indicates that the pen of a graphics table is touching or
--   not touching the tablet.
type EventProximity = Event

-- | Parts of the window have been exposed or obscured.
type EventVisibility = Event

-- | The window state has changed.
type EventWindowState = Event

-- | A grab has been broken by unusual means.
type EventGrabBroken = Event

-- | Events that are delivered to a widget.
--
-- * Any given signal only emits one of these variants as described
--   in 'Graphics.UI.Gtk.Abstract.Widget.Widget'.
--   Many events share common attributes:
--
--   * The 'eventSent' attribute is @True@ if the event was not created by the
--      user but by another application.
--
--   * The 'eventTime' attribute contains a time in milliseconds when the event
--      happened.
--
--   * The 'eventX' and 'eventY' attributes contain the coordinates relative
--      to the 'Graphics.UI.Gtk.Abstract.Gdk.DrawWindow' associated with this
--      widget. The values can contain sub-pixel information if the input
--      device is a graphics tablet or the like.
--
--   * The 'eventModifier' attribute denotes what modifier key was pressed
--      during the event.
--
data Event =
  -- | An event that is not in one of the more specific categories below. This
  -- includes delete, destroy, map and unmap events. These events
  -- have no extra information associated with them.
  Event { eventSent :: Bool }
  -- | The expose event.
  --
  -- * A region of widget that receives this event needs to be redrawn.
  --   This event is the result of revealing part or all of a window
  --   or by the application calling functions like
  --   'Graphics.UI.Gtk.Abstract.Widget.widgetQueueDrawArea'.
  --
  | Expose {
    eventSent   :: Bool,
    -- | A bounding box denoting what needs to be updated. For a more
    -- detailed information on the area that needs redrawing, use the
    -- next field.
    eventArea   :: Rectangle,
#if GTK_MAJOR_VERSION < 3
    -- | A set of horizontal stripes that denote the invalid area.
    eventRegion      :: Region,
#endif

    -- | The number of contiguous 'Expose' events following this
    --   one. The only use for this is \"exposure compression\", i.e.
    --   handling all contiguous 'Expose' events in one go, though Gdk
    --   performs some exposure compression so this is not normally needed.
    eventCount  :: Int }
  -- | Mouse motion.
  --
  -- * Captures the movement of the mouse cursor while it is within the area
  --   of the widget.
  --
  | Motion {
    eventSent   :: Bool,
    eventTime   :: TimeStamp,
    eventX,eventY       :: Double,
    eventModifier       :: [Modifier],
    -- | Indicate if this event is only a hint of the motion.
    --
    -- * If the 'Graphics.UI.Gtk.Abstract.Widget.PointerMotionHintMask'
    --  is set with 'Data.Array.MArray.widgetAddEvents' then
    --   mouse positions are only generated each time
    --  'Graphics.UI.Gtk.Gdk.DrawWindow.drawWindowGetPointer'
    --   is called. In this case 'eventIsHint' is set to @True@.
    --
    eventIsHint :: Bool,
    eventXRoot,
    eventYRoot  :: Double }
  -- | A mouse button was pressed or released.
  --
  -- * This event is triggered if the mouse button was pressed or released
  --   while the mouse cursor was within the region of the widget.
  --
  | Button {
    eventSent   :: Bool,
    -- | The kind of button press, see 'Click'. Note that double clicks will
    --   trigger this event with 'eventClick' set to 'SingleClick',
    --   'ReleaseClick',
    --   'SingleClick', 'DoubleClick', 'ReleaseClick'. Triple clicks will
    --   produce this sequence followed by 'SingleClick', 'DoubleClick',
    --   'TripleClick', 'ReleaseClick'.
    eventClick  :: Click,
    -- | The time of the event in milliseconds.
    eventTime   :: TimeStamp,
    eventX,eventY       :: Double,
    eventModifier       :: [Modifier],
    -- | The button that was pressed.
    eventButton :: MouseButton,
    -- | The coordinates of the click relative to the screen origin.
    eventXRoot,
    eventYRoot  :: Double }
  -- | A key was pressed while the widget had the input focus.
  --
  -- * If the widget has the current input focus (see
  --   'Graphics.UI.Gtk.Abstract.Widget.widgetSetCanFocus')
  --   it will receive key pressed events. Certain key combinations are of
  --   no interest to a normal widget like Alt-F to access the file menu.
  --   For all these keys, the handler must return @False@ to indicate that
  --   the key stroke should be propagated to the parent widget. At the
  --   top-level widget, keyboard shortcuts like Alt-F are turned into the
  --   corresponding signals.
  --
  | Key {
    -- | This flag is set if the key was released. This flag makes it possible
    --   to connect the same handler to
    --  'Graphics.UI.Gtk.Abstract.Widget.onKeyPress' and
    --  'Graphics.UI.Gtk.Abstract.Widget.onKeyRelease'.
    eventRelease        :: Bool,
    eventSent   :: Bool,
    eventTime   :: TimeStamp,
    eventModifier       :: [Modifier],
    -- | This flag is @True@ if Caps Lock is on while this key was pressed.
    eventWithCapsLock   :: Bool,
    -- | This flag is @True@ if Number Lock is on while this key was pressed.
    eventWithNumLock    :: Bool,
    -- | This flag is @True@ if Scroll Lock is on while this key was pressed.
    eventWithScrollLock :: Bool,
    -- | A number representing the key that was pressed or released. A more convenient
    --   interface is provided by the next two fields.
    eventKeyVal :: KeyVal,
    -- | A string representing the key that was pressed or released.
    --
    -- * This string contains a description of the key rather than what
    --   should appear on screen. For example, pressing "1" on the keypad
    --   results in "KP_1". Of particular interest are "F1" till "F12",
    --   for a complete list refer to \"<gdk/gdkkeysyms.h>\" where all
    --   possible values are defined. The corresponding strings are the
    --   constants without the GDK_ prefix.
    eventKeyName :: DefaultGlibString,
    -- | A character matching the key that was pressed.
    --
    -- * This entry can be used to build up a whole input string.
    --   The character is @Nothing@ if the key does not correspond to a simple
    --   unicode character.
    --
    eventKeyChar     :: Maybe Char }
  -- | Mouse cursor crossing event.
  --
  -- * This event indicates that the mouse cursor is hovering over this
  --   widget. It is used to set a widget into the pre-focus state where
  --   some GUI elements like buttons on a toolbar change their appearance.
  --
  | Crossing {
    eventSent   :: Bool,
    eventTime   :: TimeStamp,
    eventX,eventY       :: Double,
    eventXRoot,
    eventYRoot  :: Double,
    -- | This flag is false if the widget was entered, it is true when the
    --   widget the mouse cursor left the widget.
    eventLeaves :: Bool,
    -- | Kind of enter\/leave event.
    --
    -- * The mouse cursor might enter this widget because it grabs the mouse
    --   cursor for e.g. a modal dialog box.
    --
    eventCrossingMode   :: CrossingMode,
    -- | Information on from what level of the widget hierarchy the mouse
    --   cursor came.
    --
    -- * See 'NotifyType'.
    --
    eventNotifyType     :: NotifyType,
    eventModifier       :: [Modifier]}
  -- | Gaining or losing input focus.
  --
  | Focus {
    eventSent   :: Bool,
    -- | This flag is @True@ if the widget receives the focus and @False@ if
    -- it just lost the input focus.
    eventInFocus        :: Bool}
  -- | The widget\'s size has changed.
  --
  -- * In response to this event the application can allocate resources that
  --   are specific to the size of the widget. It is emitted when the widget
  --   is shown the first time and on every resize.
  --
  | Configure {
    eventSent   :: Bool,
    -- | Position within the parent window.
    eventXParent        :: Int,
    -- | Position within the parent window.
    eventYParent        :: Int,
    eventWidth  :: Int,
    eventHeight :: Int}
  -- | Change of visibility of a widget.
  | Visibility {
    eventSent   :: Bool,
    -- | Denote what portions of the widget is visible.
    eventVisible        :: VisibilityState }
  -- | Wheel movement of the mouse.
  --
  -- * This action denotes that the content of the widget should be scrolled.
  --   The event is triggered by the movement of the mouse wheel. Surrounding
  --   scroll bars are independent of this signal. Most mice do not have
  --   buttons for horizontal scrolling, hence 'eventDirection' will usually not
  --   contain 'ScrollLeft' and 'ScrollRight'. Mice with additional
  --   buttons may not work on X since only five buttons are supported
  --   (the three main buttons and two for the wheel).
  --
  -- * The handler of this signal should update the scroll bars that
  --   surround this widget which in turn tell this widget to update.
  --
  | Scroll {
    eventSent   :: Bool,
    eventTime   :: TimeStamp,
    eventX,eventY       :: Double,
    eventDirection      :: ScrollDirection,
    eventXRoot,
    eventYRoot  :: Double}
  -- | Indicate how the appearance of this window has changed.
  | WindowState {
    eventSent   :: Bool,
    -- | The mask indicates which flags have changed.
    eventWindowMask     :: [WindowState],
    -- | The state indicates the current state of the window.
    eventWindowState    :: [WindowState]}
  -- | The state of the pen of a graphics tablet pen or touchscreen device.
  | Proximity {
    eventSent   :: Bool,
    eventTime   :: TimeStamp,
    -- | Whether the stylus has moved in or out of contact with the tablet.
    eventInContact     :: Bool
  } deriving Show

marshalEvent :: Ptr Event -> IO Event
marshalEvent ptr = do
  (eType::#gtk2hs_type GdkEventType) <- #{peek GdkEventAny,type} ptr
  (case eType of
    #{const GDK_DELETE}         -> marshAny
    #{const GDK_DESTROY}        -> marshAny
    #{const GDK_EXPOSE}         -> marshExpose
    #{const GDK_MOTION_NOTIFY}  -> marshMotion
    #{const GDK_BUTTON_PRESS}   -> marshButton SingleClick
    #{const GDK_2BUTTON_PRESS}  -> marshButton DoubleClick
    #{const GDK_3BUTTON_PRESS}  -> marshButton TripleClick
    #{const GDK_BUTTON_RELEASE} -> marshButton ReleaseClick
    #{const GDK_KEY_PRESS}      -> marshKey False
    #{const GDK_KEY_RELEASE}    -> marshKey True
    #{const GDK_ENTER_NOTIFY}   -> marshCrossing False
    #{const GDK_LEAVE_NOTIFY}   -> marshCrossing True
    #{const GDK_FOCUS_CHANGE}   -> marshFocus
    #{const GDK_CONFIGURE}      -> marshConfigure
    #{const GDK_MAP}            -> marshAny
    #{const GDK_UNMAP}          -> marshAny
--    #{const GDK_PROPERTY_NOTIFY}-> marshProperty
    #{const GDK_PROXIMITY_IN}   -> marshProximity True
    #{const GDK_PROXIMITY_OUT}  -> marshProximity False
    #{const GDK_VISIBILITY_NOTIFY}-> marshVisibility
    #{const GDK_SCROLL}         -> marshScroll
    #{const GDK_WINDOW_STATE}   -> marshWindowState
    code                        -> \_ -> fail $
      "marshalEvent: unhandled event type " ++ show code ++
      "\nplease report this as a bug to gtk2hs-devel@lists.sourceforge.net"
    ) ptr

marshAny ptr = do
  (sent   ::#gtk2hs_type gint8) <- #{peek GdkEventAny, send_event} ptr
  return Event {
    eventSent = toBool sent
  }

marshExpose ptr = do
  (#{const GDK_EXPOSE}::#gtk2hs_type GdkEventType) <- #{peek GdkEventAny,type} ptr
  (sent_   ::#gtk2hs_type gint8)        <- #{peek GdkEventExpose, send_event} ptr
  (area_   ::Rectangle)         <- #{peek GdkEventExpose, area} ptr
#if GTK_MAJOR_VERSION < 3
  (reg_   :: Ptr Region)        <- #{peek GdkEventExpose, region} ptr
  reg_ <- gdk_region_copy reg_
  region_ <- makeNewRegion reg_
#endif
  (count_  ::#gtk2hs_type gint) <- #{peek GdkEventExpose, count} ptr
  return $ Expose {
    eventSent   = toBool sent_,
    eventArea   = area_,
#if GTK_MAJOR_VERSION < 3
    eventRegion = region_,
#endif
    eventCount  = fromIntegral count_}

#if GTK_MAJOR_VERSION < 3
foreign import ccall "gdk_region_copy"
  gdk_region_copy :: Ptr Region -> IO (Ptr Region)
#endif

marshExposeRect :: Ptr Event -> IO Rectangle
marshExposeRect ptr = do
  (#{const GDK_EXPOSE}::#gtk2hs_type GdkEventType) <- #{peek GdkEventAny,type} ptr
  (area_   ::Rectangle)         <- #{peek GdkEventExpose, area} ptr
  return area_

marshMotion ptr = do
  (sent_   ::#gtk2hs_type gint8)        <- #{peek GdkEventMotion, send_event} ptr
  (time_   ::#gtk2hs_type guint32)      <- #{peek GdkEventMotion, time} ptr
  (x_      ::#gtk2hs_type gdouble)      <- #{peek GdkEventMotion, x} ptr
  (y_      ::#gtk2hs_type gdouble)      <- #{peek GdkEventMotion, y} ptr
  (modif_  ::#gtk2hs_type guint)        <- #{peek GdkEventMotion, state} ptr
  (isHint_ ::#gtk2hs_type gint16)       <- #{peek GdkEventMotion, is_hint} ptr
  (xRoot_  ::#gtk2hs_type gdouble)      <- #{peek GdkEventMotion, x_root} ptr
  (yRoot_  ::#gtk2hs_type gdouble)      <- #{peek GdkEventMotion, y_root} ptr
  return $ Motion {
    eventSent   = toBool sent_,
    eventTime   = fromIntegral time_,
    eventX         = realToFrac x_,
    eventY         = realToFrac y_,
    eventModifier  = (toFlags . fromIntegral) modif_,
    eventIsHint = toBool isHint_,
    eventXRoot  = realToFrac xRoot_,
    eventYRoot  = realToFrac yRoot_}

marshButton but ptr = do
  (sent_   ::#gtk2hs_type gint8)        <- #{peek GdkEventButton, send_event} ptr
  (time_   ::#gtk2hs_type guint32)      <- #{peek GdkEventButton, time} ptr
  (x_      ::#gtk2hs_type gdouble)      <- #{peek GdkEventButton, x} ptr
  (y_      ::#gtk2hs_type gdouble)      <- #{peek GdkEventButton, y} ptr
  (modif_  ::#gtk2hs_type guint)        <- #{peek GdkEventButton, state} ptr
  (button_ ::#gtk2hs_type guint)        <- #{peek GdkEventButton, button} ptr
  (xRoot_  ::#gtk2hs_type gdouble)      <- #{peek GdkEventButton, x_root} ptr
  (yRoot_  ::#gtk2hs_type gdouble)      <- #{peek GdkEventButton, y_root} ptr
  return $ Button {
    eventClick  = but,
    eventSent   = toBool sent_,
    eventTime   = fromIntegral time_,
    eventX         = realToFrac x_,
    eventY         = realToFrac y_,
    eventModifier  = (toFlags . fromIntegral) modif_,
    eventButton = (toEnum.fromIntegral) button_,
    eventXRoot  = realToFrac xRoot_,
    eventYRoot  = realToFrac yRoot_}


marshKey up ptr = do
  (sent_   ::#gtk2hs_type gint8)        <- #{peek GdkEventKey, send_event} ptr
  (time_   ::#gtk2hs_type guint32)      <- #{peek GdkEventKey, time} ptr
  (modif_  ::#gtk2hs_type guint)        <- #{peek GdkEventKey, state} ptr
  (keyval_ ::#gtk2hs_type guint)        <- #{peek GdkEventKey, keyval} ptr

  (length_ ::#gtk2hs_type gint) <- #{peek GdkEventKey, length} ptr
  keyChar <- keyvalToChar keyval_
  keyName <- unsafeInterleaveIO $ keyvalName keyval_
  return $ Key {
    eventRelease = up,
    eventSent = toBool sent_,
    eventTime   = fromIntegral time_,
    eventModifier  = (toFlags . fromIntegral) modif_,
    eventWithCapsLock = (modif_ .&. #{const GDK_LOCK_MASK})/=0,
    eventWithNumLock = (modif_ .&. #{const GDK_MOD2_MASK})/=0,
    eventWithScrollLock = (modif_ .&. #{const GDK_MOD3_MASK})/=0,
    eventKeyVal = keyval_,
    eventKeyName = keyName,
    eventKeyChar = keyChar }

marshCrossing leave ptr = do
  (sent_   ::#gtk2hs_type gint8)        <- #{peek GdkEventCrossing, send_event} ptr
  (time_   ::#gtk2hs_type guint32)      <- #{peek GdkEventCrossing, time} ptr
  (x_      ::#gtk2hs_type gdouble)      <- #{peek GdkEventCrossing, x} ptr
  (y_      ::#gtk2hs_type gdouble)      <- #{peek GdkEventCrossing, y} ptr
  (modif_  ::#gtk2hs_type guint)        <- #{peek GdkEventCrossing, state} ptr
  (xRoot_  ::#gtk2hs_type gdouble)      <- #{peek GdkEventCrossing, x_root} ptr
  (yRoot_  ::#gtk2hs_type gdouble)      <- #{peek GdkEventCrossing, y_root} ptr
  (cMode_  ::#gtk2hs_type GdkCrossingMode)
                                <- #{peek GdkEventCrossing, mode} ptr
  (nType_  ::#gtk2hs_type GdkNotifyType)
                                <- #{peek GdkEventCrossing, detail} ptr
  (modif_  ::#gtk2hs_type guint)        <- #{peek GdkEventCrossing, state} ptr
  return $ Crossing {
    eventSent   = toBool sent_,
    eventTime   = fromIntegral time_,
    eventX         = realToFrac x_,
    eventY         = realToFrac y_,
    eventXRoot  = realToFrac xRoot_,
    eventYRoot  = realToFrac yRoot_,
    eventLeaves = leave,
    eventCrossingMode  = (toEnum.fromIntegral) cMode_,
    eventNotifyType    = (toEnum.fromIntegral) nType_,
    eventModifier      = (toFlags . fromIntegral) modif_}


marshFocus ptr = do
  (sent_   ::#gtk2hs_type gint8)        <- #{peek GdkEventFocus, send_event} ptr
  (inFocus_::#gtk2hs_type gint16)       <- #{peek GdkEventFocus, in} ptr
  return $ Focus {
    eventSent   = toBool sent_,
    eventInFocus= toBool inFocus_}

marshConfigure ptr = do
  (sent_   ::#gtk2hs_type gint8)        <- #{peek GdkEventConfigure, send_event} ptr
  (xPar_   ::#gtk2hs_type gint) <- #{peek GdkEventConfigure, x} ptr
  (yPar_   ::#gtk2hs_type gint) <- #{peek GdkEventConfigure, y} ptr
  (width_  ::#gtk2hs_type gint) <- #{peek GdkEventConfigure, width} ptr
  (height_ ::#gtk2hs_type gint) <- #{peek GdkEventConfigure, height} ptr
  return $ Configure {
    eventSent   = toBool sent_,
    eventXParent   = fromIntegral xPar_,
    eventYParent   = fromIntegral yPar_,
    eventWidth  = fromIntegral width_,
    eventHeight = fromIntegral height_}

{-
marshProperty ptr = do
  (sent_   ::#gtk2hs_type gint8)        <- #{peek GdkEventProperty, send_event} ptr
  (time_   ::#gtk2hs_type guint32)      <- #{peek GdkEventProperty, time} ptr
  return $ Property {
    eventSent   = toBool sent_,
    eventTime   = fromIntegral time_}
-}

marshProximity contact ptr = do
  (sent_   ::#gtk2hs_type gint8)        <- #{peek GdkEventProximity, send_event} ptr
  (time_   ::#gtk2hs_type guint32)      <- #{peek GdkEventProximity, time} ptr
  return $ Proximity {
    eventSent   = toBool sent_,
    eventTime   = fromIntegral time_,
    eventInContact = contact}

marshVisibility ptr = do
  (sent_   ::#gtk2hs_type gint8)        <- #{peek GdkEventVisibility, send_event} ptr
  (state_  ::#gtk2hs_type GdkVisibilityState)
                                <- #{peek GdkEventVisibility, state} ptr
  return $ Visibility {
    eventSent   = toBool sent_,
    eventVisible= (toEnum.fromIntegral) state_}

marshScroll ptr = do
  (sent_   ::#gtk2hs_type gint8)        <- #{peek GdkEventScroll, send_event} ptr
  (time_   ::#gtk2hs_type guint32)      <- #{peek GdkEventScroll, time} ptr
  (x_     ::#gtk2hs_type gdouble)       <- #{peek GdkEventScroll, x} ptr
  (y_     ::#gtk2hs_type gdouble)       <- #{peek GdkEventScroll, y} ptr
  (direc_  ::#gtk2hs_type GdkScrollDirection)
                                <- #{peek GdkEventScroll, direction} ptr
  (xRoot_  ::#gtk2hs_type gdouble)      <- #{peek GdkEventScroll, x_root} ptr
  (yRoot_  ::#gtk2hs_type gdouble)      <- #{peek GdkEventScroll, y_root} ptr
  return $ Scroll {
    eventSent   = toBool sent_,
    eventTime   = fromIntegral time_,
    eventX         = realToFrac x_,
    eventY         = realToFrac y_,
    eventDirection  = (toEnum.fromIntegral) direc_,
    eventXRoot  = realToFrac xRoot_,
    eventYRoot  = realToFrac yRoot_}


marshWindowState ptr = do
  (sent_   ::#gtk2hs_type gint8)        <- #{peek GdkEventWindowState, send_event} ptr
  (wMask_  ::#gtk2hs_type GdkWindowState)
                        <- #{peek GdkEventWindowState, changed_mask} ptr
  (wState_ ::#gtk2hs_type GdkWindowState)
                        <- #{peek GdkEventWindowState, new_window_state} ptr
  return $ WindowState {
    eventSent   = toBool sent_,
    eventWindowMask  = (toFlags.fromIntegral) wMask_,
    eventWindowState = (toFlags.fromIntegral) wState_}

