clutterhs-0.1: Graphics/UI/Clutter/Event.chs
-- -*-haskell-*-
-- ClutterEvent
--
-- Author : Matthew Arsenault
--
-- Created: 19 Sep 2009
--
-- Copyright (C) 2009 Matthew Arsenault
--
-- 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 3 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.
--
{-# LANGUAGE ForeignFunctionInterface #-}
{-# OPTIONS_HADDOCK prune #-}
#include <clutter/clutter.h>
{# context lib="clutter" prefix="clutter" #}
--TODO: Note the low level stuff ignored here
--TODO: This is in need of a lot of testing
--TODO: Make stuff here more consistent with itself,
--struct using vs. functions. Don't need to do silly struct things.
module Graphics.UI.Clutter.Event (
-- * Types
ModifierType(..),
ScrollDirection(..),
InputDeviceType(..),
EventType(..),
EventFlags(..),
StageState(..),
InputDevice,
DeviceID,
Timestamp,
-- * Event monad, and type tags
EventM,
EAny,
EButton,
EKey,
EMotion,
EScroll,
EStageState,
ECrossing,
-- * Accessor functions for event information
eventCoordinates,
eventState,
eventTime,
eventSource,
eventStage,
eventFlags,
eventButton,
eventClickCount,
eventKeySymbol,
eventKeyCode,
eventKeyUnicode,
eventRelated,
eventScrollDirection,
keysymToUnicode,
eventDevice,
eventDeviceId,
eventDeviceType,
-- * Misc
eventPut,
getCurrentEventTime,
eventsPending,
getInputDeviceForId,
inputDeviceGetDeviceId,
inputDeviceGetDeviceType,
tryEvent,
stopEvent,
eventM --should be private
) where
{# import Graphics.UI.Clutter.Types #}
{# import Graphics.UI.Clutter.Signals #}
{# import Graphics.UI.Clutter.Utility #}
--FIXME: Conflict with EventType Nothing
import Prelude hiding (Nothing, catch)
import qualified Prelude as P
import C2HS
import System.Glib.Signals
import System.Glib.Flags
import Control.Monad (liftM)
import Control.Monad.Trans (liftIO)
import Control.Monad.Reader (ReaderT, ask, runReaderT)
import Data.List (isPrefixOf)
import Control.Exception (Handler(..),
PatternMatchFail(..),
catches,
throw)
import System.IO.Error (isUserError, ioeGetErrorString)
--TODO: Move keyval
--Taken almost straight from Gtk. Should I try something else?
-- | A monad providing access to data in an event.
--
type EventM t a = ReaderT (Ptr t) IO a
-- | A tag for events that do not carry any event-specific information.
data EAny = EAny
-- | A tag for /Button/ events.
data EButton = EButton
-- | A tag for /key/ events.
data EKey = EKey
-- | A tag for /Motion/ events.
data EMotion = EMotion
-- | A tag for /Scroll/ events.
data EScroll = EScroll
-- | A tag for /StageState/ events.
data EStageState = EStageState
-- | A tag for /Crossing/ events.
data ECrossing = ECrossing
--Clutter seems to not have the event mask stuff
eventM :: ActorClass a => SignalName ->
ConnectAfter -> a -> (EventM t Bool) -> IO (ConnectId a)
eventM name after obj fun = connect_PTR__BOOL name after obj (runReaderT fun)
-- | Execute an event handler and assume it handled the event unless it
-- threw a pattern match exception.
--TODO: Support Old GHC exceptions here?
--Other exceptions? Why does gdk use pattern?
tryEvent :: EventM any () -> EventM any Bool
tryEvent act = do
ptr <- ask
liftIO $ (runReaderT (act >> return True) ptr)
`catches` [ Handler (\ (PatternMatchFail _) -> return False),
Handler (\ e -> if isUserError e && "Pattern" `isPrefixOf` ioeGetErrorString e
then return False
else throw e) ]
-- | Explicitly stop the handling of an event. This function should only be
-- called inside a handler that is wrapped with 'tryEvent'. (It merely
-- throws a bogus pattern matching error which 'tryEvent' interprets as if
-- the handler does not handle the event.)
stopEvent :: EventM any ()
stopEvent =
liftIO $ throw (PatternMatchFail "EventM.stopEvent called explicitly")
class HasCoordinates a
instance HasCoordinates EButton
instance HasCoordinates EMotion
instance HasCoordinates EScroll
instance HasCoordinates ECrossing
class HasModifierType a
instance HasModifierType EButton
instance HasModifierType EKey
instance HasModifierType EMotion
instance HasModifierType EScroll
--FIXME: Evil casting
-- | Retrieves the 'EventFlags' of event
--
-- the event flags
--
-- * Since 1.0
--
eventFlags :: EventM t [EventFlags]
eventFlags = ask >>= \ptr ->
liftIO $ liftM cToFlags ({# call unsafe event_get_flags #} (castPtr ptr))
-- | Retrieves the source 'Stage' the event originated for, or
-- @Nothing@ if the event has no stage.
--
-- * Since 0.8
--
eventStage :: EventM t (Maybe Stage)
eventStage = ask >>= \ptr ->
liftIO $ maybeNewStage . castPtr =<< {# call unsafe event_get_stage #} (castPtr ptr)
-- | Retrieves the source 'Actor' the event originated from, or
-- @Nothing@ if the event has no source.
--
-- * Since 0.6
--
eventSource :: EventM t (Maybe Actor)
eventSource = ask >>= \ptr ->
liftIO $ maybeNewActor =<< {# call unsafe event_get_source #} (castPtr ptr)
-- | Retrieves the related actor of a crossing event.
--
-- @Just@ the related 'Actor', or @Nothing@. transfer none.
-- * Since 1.0
--
eventRelated :: EventM ECrossing (Maybe Actor)
eventRelated = ask >>= \ptr ->
liftIO $ maybeNewActor =<< {# call unsafe event_get_related #} (castPtr ptr)
-- | Retrieve the @(x,y)@ coordinates of the mouse.
eventCoordinates :: HasCoordinates t => EventM t (Float, Float)
eventCoordinates = ask >>= \ptr ->
liftIO $ alloca $ \xptr ->
alloca $ \yptr -> do
{# call unsafe event_get_coords #} (castPtr ptr) xptr yptr
x <- peekFloatConv xptr
y <- peekFloatConv yptr
return (x,y)
-- | The time of the event, or CLUTTER_CURRENT_TIME
--
-- * Since 0.4
--
eventTime :: EventM t Timestamp
eventTime = ask >>= \ptr ->
liftIO $ liftM fromIntegral ({# call unsafe event_get_time #} (castPtr ptr))
-- | Retrieves the modifier state of the event.
--
-- the modifier state parameters, or []
--
-- * Since 0.4
--
eventState :: HasModifierType t => EventM t [ModifierType]
eventState = do
ptr <- ask
liftIO $
liftM (toModFlags . cIntConv) $ {# call unsafe event_get_state #} (castPtr ptr)
-- | Retrieves the button number of event
--
-- the button number
--
-- * Since 1.0
--
eventButton :: EventM EButton Word32
eventButton = ask >>= \ptr ->
liftIO $ liftM cIntConv ({# call unsafe event_get_button #} (castPtr ptr))
-- | Retrieves the number of clicks of event
--
-- the click count
--
-- * Since 1.0
--
eventClickCount :: EventM EButton Word
eventClickCount = ask >>= \ptr ->
liftIO $ liftM cIntConv ({# call unsafe event_get_click_count #} (castPtr ptr))
-- | Retrieves the key symbol of event
--
-- the key symbol representing the key
--
-- * Since 1.0
--
eventKeySymbol :: EventM EKey Word
eventKeySymbol = ask >>= \ptr ->
liftIO $ liftM cIntConv ({# call unsafe event_get_key_symbol #} (castPtr ptr))
-- | Retrieves the keycode of the key that caused event
--
-- The keycode representing the key
--
-- * Since 1.0
--
eventKeyCode :: EventM EKey Word16
eventKeyCode = ask >>= \ptr ->
liftIO $ liftM cIntConv ({# get ClutterKeyEvent->hardware_keycode #} ptr)
-- | Retrieves the unicode value for the key that caused keyev.
--
-- The unicode value representing the key
--
eventKeyUnicode :: EventM EKey Word32
eventKeyUnicode = ask >>= \ptr ->
liftIO $ liftM cIntConv ({# get ClutterKeyEvent->unicode_value #} ptr)
-- | Retrieves the direction of the scrolling of event
--
-- the scrolling direction
--
-- * Since 1.0
--
eventScrollDirection :: EventM EScroll ScrollDirection
eventScrollDirection = ask >>= \ptr ->
liftIO $ liftM cToEnum ({# get ClutterScrollEvent->direction #} ptr)
--TODO: Should this happen automatically? unicode char?
{# fun unsafe keysym_to_unicode as ^ { cIntConv `Word' } -> `Word' cIntConv #}
-- | Retrieves the 'InputDevice' from its id.
--
-- [@id@] a device id
--
-- [@Returns@] @Just@ an 'InputDevice', or @Nothing@. transfer none.
--
-- * Since 0.8
--
getInputDeviceForId :: DeviceID -> IO (Maybe InputDevice)
getInputDeviceForId did = let cdid = cIntConv did
in do
dev <- {# call unsafe get_input_device_for_id #} cdid
return $ if dev == nullPtr
then P.Nothing
else Just (castPtr dev)
-- | Retrieves the unique identifier of device
--
-- [@device@] an 'InputDevice'
--
-- [@Returns@] the identifier of the device
--
-- * Since 1.0
--
{# fun unsafe input_device_get_device_id as ^ { castPtr `InputDevice' } -> `DeviceID' cIntConv #}
-- | Retrieves the type of device
--
-- [@device@] an 'InputDevice'
--
-- [@Returns@] the type of the device
--
-- * Since 1.0
--
{# fun unsafe input_device_get_device_type as ^ { castPtr `InputDevice' } -> `InputDeviceType' cToEnum #}
eventDevice :: EventM t (Maybe InputDevice)
eventDevice = ask >>= \ptr ->
liftIO $ do
device <- {# call unsafe event_get_device #} (castPtr ptr)
return $ if device == nullPtr
then P.Nothing
else Just (castPtr device)
data InputDeviceStruct = InputDeviceStruct
{# pointer *ClutterInputDevice as InputDevice -> InputDeviceStruct #}
-- | Retrieves the events device id if set.
--
-- @Just@ A unique identifier for the device or @Nothing@ if the event
-- has no specific device set.
--
eventDeviceId :: EventM any (Maybe DeviceID)
eventDeviceId = ask >>= \ptr ->
liftIO $ do
did <- liftM cIntConv $ {# call unsafe event_get_device_id #} (castPtr ptr)
return $ if did == (-1)
then P.Nothing
else Just did
-- | Retrieves the type of the device for event
--
-- the 'InputDeviceType' for the device, if any is set
--
-- * Since 1.0
--
eventDeviceType :: EventM any InputDeviceType
eventDeviceType = ask >>= \ptr ->
liftIO $ liftM cToEnum $ {# call unsafe event_get_device_type #} (castPtr ptr)
-- | Retrieves the timestamp of the last event, if there is an event
-- or if the event has a timestamp.
--
-- [@Returns@] the event timestamp, or CLUTTER_CURRENT_TIME
--
-- * Since 1.0
--
{# fun unsafe get_current_event_time as ^ { } -> `Timestamp' cIntConv #}
-- | Checks if events are pending in the event queue.
--
-- [@Returns@] @True@ if there are pending events, @False@ otherwise.
--
-- * Since 0.4
--
{# fun unsafe events_pending as ^ { } -> `Bool' #}
--CHECKME: Bad?
-- | Puts a copy of the event on the back of the event queue. The
-- event will have the 'EventFlagSynthetic' flag set. If the source
-- is set event signals will be emitted for this source and
-- capture/bubbling for its ancestors. If the source is not set it
-- will be generated by picking or use the actor that currently has
-- keyboard focus
--
-- * Since 0.6
--
eventPut :: EventM any ()
eventPut = ask >>= \ptr -> liftIO $
{# call unsafe event_put #} (castPtr ptr)