packages feed

clutterhs-0.1: Graphics/UI/Clutter/Stage.chs

-- -*-haskell-*-
--  Clutter Stage
--
--  Author : Matthew Arsenault
--
--  Created: 11 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 #-}

#include <clutter/clutter.h>
#include <glib.h>

{# context lib="clutter" prefix="clutter" #}

--FIXME: remove reference to freeing stuff
-- | 'Stage' — Top level visual element to which actors are placed.
module Graphics.UI.Clutter.Stage (
-- * Detail
-- | ClutterStage is a top level 'window' on which child actors are placed and manipulated.
--
-- Clutter creates a default stage upon initialization, which can be
-- retrieved using 'stageGetDefault'. Clutter always
-- provides the default stage, unless the backend is unable to create
-- one. The stage returned by 'stageGetDefault' is
-- guaranteed to always be the same.
--
-- Backends might provide support for multiple stages. The support for
-- this feature can be checked at run-time using the
-- 'clutterFeatureAvailable' function and the
-- CLUTTER_FEATURE_STAGE_MULTIPLE flag. If the backend used supports
-- multiple stages, new 'Stage' instances can be created using
-- 'stageNew'. These stages must be managed by the developer
-- using 'actorDestroy', which will take care of destroying
-- all the actors contained inside them.
--
-- 'Stage' is a proxy actor, wrapping the backend-specific
-- implementation of the windowing system. It is possible to subclass
-- 'Stage', as long as every overridden virtual function chains up
-- to the parent class corresponding function.

-- * Class Hierarchy
-- |
-- @
-- |  'GObject'
-- |    +----'Actor'
-- |          +----'Group'
-- |                 +----'Stage'
-- @

-- * Types
  Stage,
  StageClass,
  Perspective(..),
  Fog(..),
  PickMode(..),

-- * Constructors
  stageGetDefault,
  stageNew,

  -- * Methods,
  stageIsDefault,

  stageSetColor,
  stageGetColor,

  stageSetFullscreen,
  stageGetFullscreen,

  stageShowCursor,
  stageHideCursor,

  stageGetActorAtPos,

  stageEnsureCurrent,
  stageEnsureViewport,

  stageEnsureRedraw,
  stageQueueRedraw,

--stageEvent,
  stageSetKeyFocus,
  stageGetKeyFocus,
--stageKeyFocus,
--stageReadPixels,

  stageSetThrottleMotionEvents,
  stageGetThrottleMotionEvents,

  stageSetPerspective,
  stageGetPerspective,

  stageSetTitle,
  stageGetTitle,

  stageSetUserResizable,
  stageGetUserResizable,

  stageSetUseFog,
  stageGetUseFog,

  stageSetFog,
  stageGetFog,

-- * Attributes
  stageColor,
  stageCursorVisible,
  stageFog,
  stageFullscreenSet,
  stageOffscreen,
  stagePerspective,
  stageTitle,
  stageUseFog,
  stageUserResizable,

-- * Signals
--FIXME: Export conflicts with Text's signals and probably other signals
  onActivate,
  afterActivate,
  activate,

  onDeactivate,
  afterDeactivate,
  deactivate,

  onFullscreen,
  afterFullscreen,
  fullscreen,

  onUnfullscreen,
  afterUnfullscreen,
  unfullscreen
  ) where

{# import Graphics.UI.Clutter.Types #}
{# import Graphics.UI.Clutter.Actor #}
{# import Graphics.UI.Clutter.Signals #}
{# import Graphics.UI.Clutter.Utility #}

import C2HS
import Control.Monad (liftM)
import System.Glib.Attributes
import System.Glib.Properties
import System.Glib.FFI (maybeNull)

-- | Returns the main stage. The default 'Stage' is a singleton, so
--   the stage will be created the first time this function is called
--   (typically, inside 'clutterInit'); all the subsequent calls to
--   'stageGetDefault' will return the same instance.
--   Clutter guarantess the existence of the default stage.
{# fun unsafe stage_get_default as ^ { } -> `Stage' newStage* #}

-- | Creates a new, non-default stage. A non-default stage is a new
--   top-level actor which can be used as another container. It works
--   exactly like the default stage, but while 'stageGetDefault' will
--   always return the same instance, you will have to keep any
--   'Stage' returned by 'stageNew'.
--
--   The ability to support multiple stages depends on the current
--   backend. Use 'clutterFeatureAvailable' and
--   CLUTTER_FEATURE_STAGE_MULTIPLE to check at runtime whether a
--   backend supports multiple stages.
--
--  [@Returns@] @Just@ a new stage, or @Nothing@ if the default
--   backend does not support multiple stages. Use 'actorDestroy' to
--   programmatically close the returned stage.
--
{# fun unsafe stage_new as ^ { } -> `Maybe Stage' maybeNewStage* #}

-- | Checks if stage is the default stage, or an instance created using
--  'stageNew' but internally using the same implementation.
--
{# fun unsafe stage_is_default as ^ `(StageClass stage)' =>
    { withStageClass* `stage' } -> `Bool' #}

-- | Sets the stage color.
{# fun unsafe stage_set_color as ^ `(StageClass stage)' =>
    { withStageClass* `stage', withColor* `Color' } -> `()' #}

-- | Retrieves the stage color.
{# fun unsafe stage_get_color as ^ `(StageClass stage)' =>
    { withStageClass* `stage', alloca- `Color' peek*} -> `()' #}


-- | Asks to place the stage window in the fullscreen or unfullscreen
--  states.
--
-- ( Note that you shouldn't assume the window is definitely full screen
--  afterward, because other entities (e.g. the user or window manager)
--  could unfullscreen it again, and not all window managers honor
--  requests to fullscreen windows.
--
--  If you want to receive notification of the fullscreen state you should
--  either use the "fullscreen" and "unfullscreen" signals, or use the
--  notify signal for the "fullscreen-set" property
{# fun unsafe stage_set_fullscreen as ^ `(StageClass stage)' =>
    { withStageClass* `stage', `Bool'} -> `()' #}
-- | Retrieves whether the stage is full screen or not
{# fun unsafe stage_get_fullscreen as ^ `(StageClass stage)' =>
    { withStageClass* `stage' } -> `Bool' #}


-- | Shows the cursor on the stage window
{# fun unsafe stage_show_cursor as ^ `(StageClass stage)' => { withStageClass* `stage' } -> `()' #}
-- | Makes the cursor invisible on the stage window
{# fun unsafe stage_hide_cursor as ^ `(StageClass stage)' => { withStageClass* `stage' } -> `()' #}

--CHECKME: Should I even include non-application functions?

-- | Checks the scene at the coordinates x and y and returns a pointer to the 'Actor' at those coordinates.
--
--  By using pick_mode it is possible to control which actors will be painted and thus available.
{# fun unsafe stage_get_actor_at_pos as ^ `(StageClass stage)' =>
       { withStageClass* `stage', cFromEnum `PickMode', `Int', `Int'} -> `Actor' newActor* #}

-- | This function essentially makes sure the right GL context is
--   current for the passed stage. It is not intended to be used by
--   applications.
{# fun unsafe stage_ensure_current as ^ `(StageClass stage)' => { withStageClass* `stage' } -> `()' #}

-- | Ensures that the GL viewport is updated with the current stage
--   window size.
--
-- This function will queue a redraw of stage.
--
-- This function should not be called by applications; it is used when
-- embedding a ClutterStage into a toolkit with another windowing
-- system, like GTK+.
{# fun unsafe stage_ensure_viewport as ^ `(StageClass stage)' => { withStageClass* `stage' } -> `()' #}

-- | Ensures that stage is redrawn
--
-- This function should not be called by applications: it is used when
-- embedding a 'Stage' into a toolkit with another windowing
-- system, like GTK+.
{# fun unsafe stage_ensure_redraw as ^ `(StageClass stage)' => { withStageClass* `stage' } -> `()' #}

-- | Queues a redraw for the passed stage.
--
-- Note
--
-- Applications should call 'actorQueueRedraw' and not this function.
-- Note
--
-- This function is just a wrapper for 'actorQueueRedraw' and should probably go away.
{# fun unsafe stage_queue_redraw as ^ `(StageClass stage)' => { withStageClass* `stage' } -> `()' #}

--CHECKME this might fall under the category of low level event stuff we're not dealing with
--{# fun unsafe stage_event as ^ `(StageClass stage)' => { withStageClass* `stage', withEvent* `Event' } -> `Bool' #}


-- | Sets the key focus on actor. An actor with key focus will receive
--   all the key events. If actor is @Nothing@, the stage will receive
--   focus.
--
-- [@stage@] the 'Stage'
--
-- [@actor@] @Just@ the actor to set key focus to, or @Nothing@
--
-- * Since 0.6
--
{# fun unsafe stage_set_key_focus as ^ `(StageClass stage, ActorClass actor)' =>
       { withStageClass* `stage', withMaybeActorClass* `Maybe actor' } -> `()' #}

-- | Retrieves the actor that is currently under key focus.
--
-- [@stage@] the 'Stage'
--
-- [@Returns@] the actor with key focus, or the stage. transfer none.
--
-- * Since 0.6
--
{# fun unsafe stage_get_key_focus as ^ `(StageClass stage)' => { withStageClass* `stage' } -> `Actor' newActor* #}

--TODO: Same problem as other places, setting and getting ActorClass is unhappy
--stageKeyFocus :: (StageClass stage, ActorClass actor) => Attr stage actor
--stageKeyFocus = newAttr stageGetKeyFocus stageSetKeyFocus

--TODO: all those types, namely guchar* out = what?
--Returns some kind of image buffer, what do I do with it?
--{# fun unsafe stage_read_pixels as ^ `(StageClass stage)' => { withStageClass* `stage', `Int', `Int', `Int', `Int' } -> `Ptr ()' #}
--Why is this scattered around in many places in gtk2hs?
foreign import ccall unsafe "&g_free"
  finalizerGFree :: FinalizerPtr a


stageReadPixels :: (StageClass stage) =>
                   stage
                   -> Int
                   -> Int
                   -> Int
                   -> Int
                   -> IO (Maybe (RGBData Int Word8))
stageReadPixels stage x y w h = let cx = cIntConv x
                                    cy = cIntConv y
                                    cw = cIntConv w
                                    ch = cIntConv h
                                in withStageClass stage $ \stgPtr -> do
                                  sizeW <- if w == -1
                                              then liftM floor (actorGetWidth stage)
                                              else return w
                                  sizeH <- if h == -1
                                              then liftM floor (actorGetHeight stage)
                                              else return h
                                  let size = sizeW * sizeH * 4
                                  ptr <- {# call unsafe stage_read_pixels #} stgPtr cx cy cw ch
                                  if ptr == nullPtr
                                     then return Prelude.Nothing
                                     else newForeignPtr finalizerGFree ptr >>= \fptr ->
                                            return $ Just (mkRGBData (castForeignPtr fptr) True size)


-- | Sets whether motion events received between redraws should be
-- throttled or not. If motion events are throttled, those events
-- received by the windowing system between redraws will be compressed
-- so that only the last event will be propagated to the stage and its
-- actors.  This function should only be used if you want to have all
-- the motion events delivered to your application code.
{# fun unsafe stage_set_throttle_motion_events as ^ `(StageClass stage)' =>
    { withStageClass* `stage', `Bool' } -> `()' #}

-- | Retrieves the value set with 'stageSetThrottleMotionEvents'
{# fun unsafe stage_get_throttle_motion_events as ^ `(StageClass stage)' =>
    { withStageClass* `stage' } -> `Bool' #}


-- | Retrieves the stage perspective.
{# fun unsafe stage_get_perspective as ^ `(StageClass stage)' =>
    { withStageClass* `stage', alloca- `Perspective' peek* } -> `()' #}
-- | Sets the stage perspective.
{# fun unsafe stage_set_perspective as ^ `(StageClass stage)' =>
    { withStageClass* `stage', withPerspective* `Perspective'} -> `()' #}



--TODO: Unicode???
-- | Sets the stage title.
{# fun unsafe stage_set_title as ^ `(StageClass stage)' =>
    { withStageClass* `stage', withMaybeString* `Maybe String' } -> `()' #}
-- | Gets the stage title.
{# fun unsafe stage_get_title as ^ `(StageClass stage)' =>
    { withStageClass* `stage' } -> `Maybe String' maybeString* #}

-- | Sets if the stage is resizable by user interaction (e.g. via window manager controls)
{# fun unsafe stage_set_user_resizable as ^ `(StageClass stage)' =>
    { withStageClass* `stage', `Bool' } -> `()' #}
-- | Retrieves the value set with 'stageSetUserResizable'.
{# fun unsafe stage_get_user_resizable as ^ `(StageClass stage)' =>
    { withStageClass* `stage' } -> `Bool' #}


-- | Sets whether the depth cueing effect on the stage should be enabled or not.
--
--   Depth cueing is a 3D effect that makes actors farther away from
--   the viewing point less opaque, by fading them with the stage
--   color.
--
-- The parameters of the GL fog used can be changed using the 'stageSetFog' function.
{# fun unsafe stage_set_use_fog as ^ `(StageClass stage)' =>
    { withStageClass* `stage', `Bool' } -> `()' #}

-- | Gets whether the depth cueing effect is enabled on stage.
{# fun unsafe stage_get_use_fog as ^ `(StageClass stage)' => { withStageClass* `stage' } -> `Bool' #}


-- | Sets the fog (also known as "depth cueing") settings for the stage.
--
--  A 'Stage' will only use a linear fog progression, which depends
--  solely on the distance from the viewer. The cogl_set_fog() function in
--  COGL exposes more of the underlying implementation, and allows
--  changing the for progression function. It can be directly used by
--  disabling the "use-fog" property and connecting a signal handler to
--  the "paint" signal on the stage, like:
-- TODO: The equivalent example
--
--  Note: The fogging functions only work correctly when the visible
--  actors use unmultiplied alpha colors. By default Cogl will premultiply
--  textures and cogl_set_source_color will premultiply colors, so unless
--  you explicitly load your textures requesting an unmultiplied
--  internal_format and use cogl_material_set_color you can only use
--  fogging with fully opaque actors.
--
-- We can look to improve this in the future when we can depend on
--  fragment shaders.
{# fun unsafe stage_set_fog as ^ `(StageClass stage)' =>
    { withStageClass* `stage', withFog* `Fog' } -> `()' #}

-- | Retrieves the current depth cueing settings from the stage.
{# fun unsafe stage_get_fog as ^ `(StageClass stage)' =>
    { withStageClass* `stage', alloca- `Fog' peek* } -> `()' #}


-- Attributes


-- | The color of the main stage.
stageColor :: (StageClass stage) => Attr stage Color
stageColor = newNamedAttr "color" stageGetColor stageSetColor


-- | Whether the mouse pointer should be visible
--
-- Default value: @True@
--
stageCursorVisible :: (StageClass stage) => Attr stage Bool
stageCursorVisible = newAttrFromBoolProperty "cursor-visible"


-- | Whether the main stage is fullscreen.
--
-- Default value: @False@
--
stageFullscreenSet :: (StageClass stage) => ReadAttr stage Bool
stageFullscreenSet = readAttrFromBoolProperty "fullscreen-set"

-- | Whether the stage should be rendered in an offscreen buffer.
--
-- * Warning
--
-- Not every backend supports redirecting the stage to an offscreen
-- buffer. This property might not work and it might be deprecated at
-- any later date.
--
-- Default value: @False@
--
stageOffscreen :: (StageClass stage) => Attr stage Bool
stageOffscreen = newAttrFromBoolProperty "offscreen"


-- | The parameters used for the perspective projection from 3D
--   coordinates to 2D
--
-- * Since 0.8.2
--
stagePerspective :: (StageClass stage) => Attr stage Perspective
stagePerspective = newNamedAttr "perspective" stageGetPerspective stageSetPerspective


-- | The stage's title - usually displayed in stage windows title
--   decorations.
--
-- Default value: @Nothing@
--
-- * Since 0.4
--
stageTitle :: (StageClass stage) => Attr stage (Maybe String)
stageTitle = newNamedAttr "title" stageGetTitle stageSetTitle


-- | Whether the stage is resizable via user interaction.
--
-- Default value: @False@
--
-- * Since 0.4
--
stageUserResizable :: (StageClass stage) => Attr stage Bool
stageUserResizable = newNamedAttr "user-resizable" stageGetUserResizable stageSetUserResizable


-- | Whether the stage should use a linear GL "fog" in creating the
--   depth-cueing effect, to enhance the perception of depth by fading
--   actors farther from the viewpoint.
--
-- Default value: @False@
--
-- * Since 0.6
--
stageUseFog :: (StageClass stage) => Attr stage Bool
stageUseFog = newNamedAttr "use-fog" stageGetUseFog stageSetUseFog


-- | The settings for the GL "fog", used only if "use-fog" is set to
--   @True@
--
-- * Since 1.0
--
stageFog :: (StageClass stage) => Attr stage Fog
stageFog = newNamedAttr "fog" stageGetFog stageSetFog


-- Signals

--See note in Types of Activatable
-- | The 'activate' signal is emitted when the stage receives key focus from the underlying window system.
instance Activatable Stage where
  onActivate = connect_NONE__NONE "activate" False
  afterActivate = connect_NONE__NONE "activate" True
  activate = Signal (connect_NONE__NONE "activate")

onDeactivate, afterDeactivate :: (StageClass stage) => stage -> IO () -> IO (ConnectId stage)
onDeactivate = connect_NONE__NONE "deactivate" False
afterDeactivate = connect_NONE__NONE "deactivate" True

-- | The 'deactivate' signal is emitted when the stage loses key focus
--   from the underlying window system.
deactivate :: (StageClass stage) => Signal stage (IO ())
deactivate = Signal (connect_NONE__NONE "deactivate")


onFullscreen, afterFullscreen :: (StageClass stage) => stage -> IO () -> IO (ConnectId stage)
onFullscreen = connect_NONE__NONE "fullscreen" False
afterFullscreen = connect_NONE__NONE "fullscreen" True

-- | The 'fullscreen' signal is emitted when the stage is made fullscreen.
fullscreen :: (StageClass stage) => Signal stage (IO ())
fullscreen = Signal (connect_NONE__NONE "fullscreen")


onUnfullscreen, afterUnfullscreen :: (StageClass stage) => stage -> IO () -> IO (ConnectId stage)
onUnfullscreen = connect_NONE__NONE "unfullscreen" False
afterUnfullscreen = connect_NONE__NONE "unfullscreen" True

-- | The 'unfullscreen' signal is emitted when the stage leaves a fullscreen state.
unfullscreen :: (StageClass stage) => Signal stage (IO ())
unfullscreen = Signal (connect_NONE__NONE "unfullscreen")