1b4ff35df5c04aec71fce7e90a6d6f9ef7180c2adJeff Brown/*
2b4ff35df5c04aec71fce7e90a6d6f9ef7180c2adJeff Brown * Copyright (C) 2010 The Android Open Source Project
3b4ff35df5c04aec71fce7e90a6d6f9ef7180c2adJeff Brown *
4b4ff35df5c04aec71fce7e90a6d6f9ef7180c2adJeff Brown * Licensed under the Apache License, Version 2.0 (the "License");
5b4ff35df5c04aec71fce7e90a6d6f9ef7180c2adJeff Brown * you may not use this file except in compliance with the License.
6b4ff35df5c04aec71fce7e90a6d6f9ef7180c2adJeff Brown * You may obtain a copy of the License at
7b4ff35df5c04aec71fce7e90a6d6f9ef7180c2adJeff Brown *
8b4ff35df5c04aec71fce7e90a6d6f9ef7180c2adJeff Brown *      http://www.apache.org/licenses/LICENSE-2.0
9b4ff35df5c04aec71fce7e90a6d6f9ef7180c2adJeff Brown *
10b4ff35df5c04aec71fce7e90a6d6f9ef7180c2adJeff Brown * Unless required by applicable law or agreed to in writing, software
11b4ff35df5c04aec71fce7e90a6d6f9ef7180c2adJeff Brown * distributed under the License is distributed on an "AS IS" BASIS,
12b4ff35df5c04aec71fce7e90a6d6f9ef7180c2adJeff Brown * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13b4ff35df5c04aec71fce7e90a6d6f9ef7180c2adJeff Brown * See the License for the specific language governing permissions and
14b4ff35df5c04aec71fce7e90a6d6f9ef7180c2adJeff Brown * limitations under the License.
15b4ff35df5c04aec71fce7e90a6d6f9ef7180c2adJeff Brown */
16b4ff35df5c04aec71fce7e90a6d6f9ef7180c2adJeff Brown
17b4ff35df5c04aec71fce7e90a6d6f9ef7180c2adJeff Brown#ifndef _UI_POINTER_CONTROLLER_H
18b4ff35df5c04aec71fce7e90a6d6f9ef7180c2adJeff Brown#define _UI_POINTER_CONTROLLER_H
19b4ff35df5c04aec71fce7e90a6d6f9ef7180c2adJeff Brown
205541de9ea3513a12d1ac2ad07e7e04a3aa7741a0Jeff Brown#include "SpriteController.h"
215541de9ea3513a12d1ac2ad07e7e04a3aa7741a0Jeff Brown
221db5397d88e722b1ab82ccb2b429ceec1179ccd8Jun Mukai#include <map>
23808196f139e93395f1f331f6c7e92ddd66c05979Jun Mukai#include <vector>
241db5397d88e722b1ab82ccb2b429ceec1179ccd8Jun Mukai
25b4ff35df5c04aec71fce7e90a6d6f9ef7180c2adJeff Brown#include <ui/DisplayInfo.h>
269d3b1a424c5c61e24e9659d15fb353026a00d925Jeff Brown#include <input/Input.h>
27d6b473713f43dec0828971854fe1018642cfaf27Michael Wright#include <inputflinger/PointerControllerInterface.h>
288a90e6e3174083f274538567d851f98478fc83e9Jeff Brown#include <utils/BitSet.h>
29b4ff35df5c04aec71fce7e90a6d6f9ef7180c2adJeff Brown#include <utils/RefBase.h>
3005dc66ada6b61a6bdf806ffaa62617ac5394695dJeff Brown#include <utils/Looper.h>
31b4ff35df5c04aec71fce7e90a6d6f9ef7180c2adJeff Brown#include <utils/String8.h>
32c0c0ac37abe6f0b1ab780765b4a48beada7d3444Jun Mukai#include <gui/DisplayEventReceiver.h>
33b4ff35df5c04aec71fce7e90a6d6f9ef7180c2adJeff Brown
34b4ff35df5c04aec71fce7e90a6d6f9ef7180c2adJeff Brownnamespace android {
35b4ff35df5c04aec71fce7e90a6d6f9ef7180c2adJeff Brown
362352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown/*
372352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown * Pointer resources.
382352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown */
392352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brownstruct PointerResources {
402352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    SpriteIcon spotHover;
412352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    SpriteIcon spotTouch;
422352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    SpriteIcon spotAnchor;
432352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown};
442352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown
45808196f139e93395f1f331f6c7e92ddd66c05979Jun Mukaistruct PointerAnimation {
46808196f139e93395f1f331f6c7e92ddd66c05979Jun Mukai    std::vector<SpriteIcon> animationFrames;
47808196f139e93395f1f331f6c7e92ddd66c05979Jun Mukai    nsecs_t durationPerFrame;
48808196f139e93395f1f331f6c7e92ddd66c05979Jun Mukai};
49808196f139e93395f1f331f6c7e92ddd66c05979Jun Mukai
502352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown/*
512352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown * Pointer controller policy interface.
522352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown *
532352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown * The pointer controller policy is used by the pointer controller to interact with
542352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown * the Window Manager and other system components.
552352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown *
562352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown * The actual implementation is partially supported by callbacks into the DVM
572352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown * via JNI.  This interface is also mocked in the unit tests.
582352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown */
592352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brownclass PointerControllerPolicyInterface : public virtual RefBase {
602352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brownprotected:
612352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    PointerControllerPolicyInterface() { }
622352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    virtual ~PointerControllerPolicyInterface() { }
632352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown
642352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brownpublic:
6519a560197950425f7e1856d5bd1216fbc680bf70Jun Mukai    virtual void loadPointerIcon(SpriteIcon* icon) = 0;
662352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    virtual void loadPointerResources(PointerResources* outResources) = 0;
67808196f139e93395f1f331f6c7e92ddd66c05979Jun Mukai    virtual void loadAdditionalMouseResources(std::map<int32_t, SpriteIcon>* outResources,
68808196f139e93395f1f331f6c7e92ddd66c05979Jun Mukai            std::map<int32_t, PointerAnimation>* outAnimationResources) = 0;
695ec7420ecd0e94be0a07fc77b0bc950deb0178fdJun Mukai    virtual int32_t getDefaultPointerIconId() = 0;
70d4eaef7f4c5a5d281de4fff272cd33e892e26264Jun Mukai    virtual int32_t getCustomPointerIconId() = 0;
71b4ff35df5c04aec71fce7e90a6d6f9ef7180c2adJeff Brown};
72b4ff35df5c04aec71fce7e90a6d6f9ef7180c2adJeff Brown
73b4ff35df5c04aec71fce7e90a6d6f9ef7180c2adJeff Brown
74b4ff35df5c04aec71fce7e90a6d6f9ef7180c2adJeff Brown/*
75b4ff35df5c04aec71fce7e90a6d6f9ef7180c2adJeff Brown * Tracks pointer movements and draws the pointer sprite to a surface.
76b4ff35df5c04aec71fce7e90a6d6f9ef7180c2adJeff Brown *
77b4ff35df5c04aec71fce7e90a6d6f9ef7180c2adJeff Brown * Handles pointer acceleration and animation.
78b4ff35df5c04aec71fce7e90a6d6f9ef7180c2adJeff Brown */
79c0c0ac37abe6f0b1ab780765b4a48beada7d3444Jun Mukaiclass PointerController : public PointerControllerInterface, public MessageHandler,
80c0c0ac37abe6f0b1ab780765b4a48beada7d3444Jun Mukai                          public LooperCallback {
81b4ff35df5c04aec71fce7e90a6d6f9ef7180c2adJeff Brownprotected:
82b4ff35df5c04aec71fce7e90a6d6f9ef7180c2adJeff Brown    virtual ~PointerController();
83b4ff35df5c04aec71fce7e90a6d6f9ef7180c2adJeff Brown
84b4ff35df5c04aec71fce7e90a6d6f9ef7180c2adJeff Brownpublic:
852352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    enum InactivityTimeout {
862352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        INACTIVITY_TIMEOUT_NORMAL = 0,
872352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        INACTIVITY_TIMEOUT_SHORT = 1,
8805dc66ada6b61a6bdf806ffaa62617ac5394695dJeff Brown    };
8905dc66ada6b61a6bdf806ffaa62617ac5394695dJeff Brown
902352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    PointerController(const sp<PointerControllerPolicyInterface>& policy,
912352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown            const sp<Looper>& looper, const sp<SpriteController>& spriteController);
92b4ff35df5c04aec71fce7e90a6d6f9ef7180c2adJeff Brown
93b4ff35df5c04aec71fce7e90a6d6f9ef7180c2adJeff Brown    virtual bool getBounds(float* outMinX, float* outMinY,
94b4ff35df5c04aec71fce7e90a6d6f9ef7180c2adJeff Brown            float* outMaxX, float* outMaxY) const;
95b4ff35df5c04aec71fce7e90a6d6f9ef7180c2adJeff Brown    virtual void move(float deltaX, float deltaY);
96fe9f8ab03a63b1037f07dd85799fbea80ec6adaaJeff Brown    virtual void setButtonState(int32_t buttonState);
97fe9f8ab03a63b1037f07dd85799fbea80ec6adaaJeff Brown    virtual int32_t getButtonState() const;
98b4ff35df5c04aec71fce7e90a6d6f9ef7180c2adJeff Brown    virtual void setPosition(float x, float y);
99b4ff35df5c04aec71fce7e90a6d6f9ef7180c2adJeff Brown    virtual void getPosition(float* outX, float* outY) const;
100538881e18323a0c983bd8809f8c3b1cdeeeab8a6Jeff Brown    virtual void fade(Transition transition);
101538881e18323a0c983bd8809f8c3b1cdeeeab8a6Jeff Brown    virtual void unfade(Transition transition);
102b4ff35df5c04aec71fce7e90a6d6f9ef7180c2adJeff Brown
1032352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    virtual void setPresentation(Presentation presentation);
104cb5ffcf0e41d2597401208221c61589547a00f3dJeff Brown    virtual void setSpots(const PointerCoords* spotCoords,
105cb5ffcf0e41d2597401208221c61589547a00f3dJeff Brown            const uint32_t* spotIdToIndex, BitSet32 spotIdBits);
1062352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    virtual void clearSpots();
1072352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown
108f9d9ce7705475874c82af04eb9b208a7fb556792Michael Wright    void updatePointerIcon(int32_t iconId);
109d4eaef7f4c5a5d281de4fff272cd33e892e26264Jun Mukai    void setCustomPointerIcon(const SpriteIcon& icon);
110d728bf514f257670fcb9aa22c6eaf97626072c93Jeff Brown    void setDisplayViewport(int32_t width, int32_t height, int32_t orientation);
1112352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    void setInactivityTimeout(InactivityTimeout inactivityTimeout);
11219a560197950425f7e1856d5bd1216fbc680bf70Jun Mukai    void reloadPointerResources();
113b4ff35df5c04aec71fce7e90a6d6f9ef7180c2adJeff Brown
114b4ff35df5c04aec71fce7e90a6d6f9ef7180c2adJeff Brownprivate:
1152352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    static const size_t MAX_RECYCLED_SPRITES = 12;
1162352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    static const size_t MAX_SPOTS = 12;
1172352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown
11805dc66ada6b61a6bdf806ffaa62617ac5394695dJeff Brown    enum {
1192352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        MSG_INACTIVITY_TIMEOUT,
1202352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    };
1212352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown
1222352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    struct Spot {
1232352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        static const uint32_t INVALID_ID = 0xffffffff;
1242352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown
1252352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        uint32_t id;
1262352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        sp<Sprite> sprite;
1272352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        float alpha;
1282352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        float scale;
1292352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        float x, y;
1302352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown
1312352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        inline Spot(uint32_t id, const sp<Sprite>& sprite)
1322352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown                : id(id), sprite(sprite), alpha(1.0f), scale(1.0f),
1332352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown                  x(0.0f), y(0.0f), lastIcon(NULL) { }
1342352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown
1352352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        void updateSprite(const SpriteIcon* icon, float x, float y);
1362352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown
1372352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    private:
1382352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        const SpriteIcon* lastIcon;
13905dc66ada6b61a6bdf806ffaa62617ac5394695dJeff Brown    };
14005dc66ada6b61a6bdf806ffaa62617ac5394695dJeff Brown
141b4ff35df5c04aec71fce7e90a6d6f9ef7180c2adJeff Brown    mutable Mutex mLock;
142b4ff35df5c04aec71fce7e90a6d6f9ef7180c2adJeff Brown
1432352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    sp<PointerControllerPolicyInterface> mPolicy;
14405dc66ada6b61a6bdf806ffaa62617ac5394695dJeff Brown    sp<Looper> mLooper;
1455541de9ea3513a12d1ac2ad07e7e04a3aa7741a0Jeff Brown    sp<SpriteController> mSpriteController;
1465541de9ea3513a12d1ac2ad07e7e04a3aa7741a0Jeff Brown    sp<WeakMessageHandler> mHandler;
14733c5903e7759b0594b1e0a062b066945a2c86989Vladislav Kaznacheev    sp<LooperCallback> mCallback;
148b4ff35df5c04aec71fce7e90a6d6f9ef7180c2adJeff Brown
149c0c0ac37abe6f0b1ab780765b4a48beada7d3444Jun Mukai    DisplayEventReceiver mDisplayEventReceiver;
150c0c0ac37abe6f0b1ab780765b4a48beada7d3444Jun Mukai
1512352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    PointerResources mResources;
1522352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown
153b4ff35df5c04aec71fce7e90a6d6f9ef7180c2adJeff Brown    struct Locked {
1542352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        bool animationPending;
1552352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        nsecs_t animationTime;
1562352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown
157808196f139e93395f1f331f6c7e92ddd66c05979Jun Mukai        size_t animationFrameIndex;
158808196f139e93395f1f331f6c7e92ddd66c05979Jun Mukai        nsecs_t lastFrameUpdatedTime;
159808196f139e93395f1f331f6c7e92ddd66c05979Jun Mukai
160b4ff35df5c04aec71fce7e90a6d6f9ef7180c2adJeff Brown        int32_t displayWidth;
161b4ff35df5c04aec71fce7e90a6d6f9ef7180c2adJeff Brown        int32_t displayHeight;
162b4ff35df5c04aec71fce7e90a6d6f9ef7180c2adJeff Brown        int32_t displayOrientation;
163b4ff35df5c04aec71fce7e90a6d6f9ef7180c2adJeff Brown
1642352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        InactivityTimeout inactivityTimeout;
1652352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown
1662352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        Presentation presentation;
1672352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        bool presentationChanged;
1682352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown
169538881e18323a0c983bd8809f8c3b1cdeeeab8a6Jeff Brown        int32_t pointerFadeDirection;
170b4ff35df5c04aec71fce7e90a6d6f9ef7180c2adJeff Brown        float pointerX;
171b4ff35df5c04aec71fce7e90a6d6f9ef7180c2adJeff Brown        float pointerY;
1722352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        float pointerAlpha;
1732352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        sp<Sprite> pointerSprite;
1742352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        SpriteIcon pointerIcon;
1752352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        bool pointerIconChanged;
176b4ff35df5c04aec71fce7e90a6d6f9ef7180c2adJeff Brown
177808196f139e93395f1f331f6c7e92ddd66c05979Jun Mukai        std::map<int32_t, SpriteIcon> additionalMouseResources;
178808196f139e93395f1f331f6c7e92ddd66c05979Jun Mukai        std::map<int32_t, PointerAnimation> animationResources;
1791db5397d88e722b1ab82ccb2b429ceec1179ccd8Jun Mukai
180f9d9ce7705475874c82af04eb9b208a7fb556792Michael Wright        int32_t requestedPointerType;
1811db5397d88e722b1ab82ccb2b429ceec1179ccd8Jun Mukai
182fe9f8ab03a63b1037f07dd85799fbea80ec6adaaJeff Brown        int32_t buttonState;
183b4ff35df5c04aec71fce7e90a6d6f9ef7180c2adJeff Brown
1842352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        Vector<Spot*> spots;
1852352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown        Vector<sp<Sprite> > recycledSprites;
1865541de9ea3513a12d1ac2ad07e7e04a3aa7741a0Jeff Brown    } mLocked;
18705dc66ada6b61a6bdf806ffaa62617ac5394695dJeff Brown
188b4ff35df5c04aec71fce7e90a6d6f9ef7180c2adJeff Brown    bool getBoundsLocked(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const;
189b4ff35df5c04aec71fce7e90a6d6f9ef7180c2adJeff Brown    void setPositionLocked(float x, float y);
19005dc66ada6b61a6bdf806ffaa62617ac5394695dJeff Brown
19105dc66ada6b61a6bdf806ffaa62617ac5394695dJeff Brown    void handleMessage(const Message& message);
192c0c0ac37abe6f0b1ab780765b4a48beada7d3444Jun Mukai    int handleEvent(int fd, int events, void* data);
193c0c0ac37abe6f0b1ab780765b4a48beada7d3444Jun Mukai    void doAnimate(nsecs_t timestamp);
194808196f139e93395f1f331f6c7e92ddd66c05979Jun Mukai    bool doFadingAnimationLocked(nsecs_t timestamp);
195808196f139e93395f1f331f6c7e92ddd66c05979Jun Mukai    bool doBitmapAnimationLocked(nsecs_t timestamp);
1962352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    void doInactivityTimeout();
1972352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown
1982352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    void startAnimationLocked();
1992352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown
2002352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    void resetInactivityTimeoutLocked();
201538881e18323a0c983bd8809f8c3b1cdeeeab8a6Jeff Brown    void removeInactivityTimeoutLocked();
2022352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    void updatePointerLocked();
2032352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown
2042352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    Spot* getSpotLocked(uint32_t id);
2052352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    Spot* createAndAddSpotLocked(uint32_t id);
2062352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    Spot* removeFirstFadingSpotLocked();
2072352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    void releaseSpotLocked(Spot* spot);
2082352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    void fadeOutAndReleaseSpotLocked(Spot* spot);
2092352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    void fadeOutAndReleaseAllSpotsLocked();
2102352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown
2112352b978a3c94cd88f41d0d908f961333fdac1e9Jeff Brown    void loadResources();
212b4ff35df5c04aec71fce7e90a6d6f9ef7180c2adJeff Brown};
213b4ff35df5c04aec71fce7e90a6d6f9ef7180c2adJeff Brown
214b4ff35df5c04aec71fce7e90a6d6f9ef7180c2adJeff Brown} // namespace android
215b4ff35df5c04aec71fce7e90a6d6f9ef7180c2adJeff Brown
216b4ff35df5c04aec71fce7e90a6d6f9ef7180c2adJeff Brown#endif // _UI_POINTER_CONTROLLER_H
217