1/*
2 * Copyright 2013 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "SampleCode.h"
9#include "SkView.h"
10#include "SkLua.h"
11#include "SkCanvas.h"
12#include "Resources.h"
13#include "SkData.h"
14
15extern "C" {
16#include "lua.h"
17#include "lualib.h"
18#include "lauxlib.h"
19}
20
21//#define LUA_FILENAME    "test.lua"
22#define LUA_FILENAME    "slides.lua"
23
24static const char gDrawName[] = "onDrawContent";
25static const char gClickName[] = "onClickHandler";
26static const char gUnicharName[] = "onCharHandler";
27
28static const char gLuaClickHandlerName[] = "lua-click-handler";
29
30static const char gMissingCode[] = ""
31    "local paint = Sk.newPaint()"
32    "paint:setAntiAlias(true)"
33    "paint:setTextSize(30)"
34    ""
35    "function onDrawContent(canvas)"
36    "   canvas:drawText('missing \"test.lua\"', 20, 50, paint)"
37    "end"
38    ;
39
40class LuaView : public SampleView {
41public:
42    LuaView() : fLua(nullptr) {}
43
44    virtual ~LuaView() { delete fLua; }
45
46    void setImageFilename(lua_State* L) {
47        SkString str = GetResourcePath("mandrill_256.png");
48
49        lua_getglobal(L, "setImageFilename");
50        if (lua_isfunction(L, -1)) {
51            fLua->pushString(str.c_str());
52            if (lua_pcall(L, 1, 0, 0) != LUA_OK) {
53                SkDebugf("lua err: %s\n", lua_tostring(L, -1));
54            }
55        }
56    }
57
58    lua_State* ensureLua() {
59        if (nullptr == fLua) {
60            fLua = new SkLua;
61
62            SkString str = GetResourcePath(LUA_FILENAME);
63            SkData* data = SkData::NewFromFileName(str.c_str());
64            if (data) {
65                fLua->runCode(data->data(), data->size());
66                data->unref();
67                this->setImageFilename(fLua->get());
68            } else {
69                fLua->runCode(gMissingCode);
70            }
71        }
72        return fLua->get();
73    }
74
75protected:
76    bool onQuery(SkEvent* evt) override {
77        if (SampleCode::TitleQ(*evt)) {
78            SampleCode::TitleR(evt, "Lua");
79            return true;
80        }
81        SkUnichar uni;
82        if (SampleCode::CharQ(*evt, &uni)) {
83            lua_State* L = this->ensureLua();
84            lua_getglobal(L, gUnicharName);
85            if (lua_isfunction(L, -1)) {
86                SkString str;
87                str.appendUnichar(uni);
88                fLua->pushString(str.c_str());
89                if (lua_pcall(L, 1, 1, 0) != LUA_OK) {
90                    SkDebugf("lua err: %s\n", lua_tostring(L, -1));
91                } else {
92                    if (lua_isboolean(L, -1) && lua_toboolean(L, -1)) {
93                        this->inval(nullptr);
94                        return true;
95                    }
96                }
97            }
98        }
99        return this->INHERITED::onQuery(evt);
100    }
101
102    void onDrawContent(SkCanvas* canvas) override {
103        lua_State* L = this->ensureLua();
104
105        lua_getglobal(L, gDrawName);
106        if (!lua_isfunction(L, -1)) {
107            int t = lua_type(L, -1);
108            SkDebugf("--- expected %s function %d, ignoring.\n", gDrawName, t);
109            lua_pop(L, 1);
110        } else {
111            // does it make sense to try to "cache" the lua version of this
112            // canvas between draws?
113            fLua->pushCanvas(canvas);
114            fLua->pushScalar(this->width());
115            fLua->pushScalar(this->height());
116            if (lua_pcall(L, 3, 1, 0) != LUA_OK) {
117                SkDebugf("lua err: %s\n", lua_tostring(L, -1));
118            } else {
119                if (lua_isboolean(L, -1) && lua_toboolean(L, -1)) {
120                    this->inval(nullptr);
121                }
122            }
123        }
124    }
125
126    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y,
127                                              unsigned modi) override {
128        lua_State* L = this->ensureLua();
129        lua_getglobal(L, gClickName);
130        if (lua_isfunction(L, -1)) {
131            fLua->pushScalar(x);
132            fLua->pushScalar(y);
133            fLua->pushString("down");
134            if (lua_pcall(L, 3, 1, 0) != LUA_OK) {
135                SkDebugf("lua err: %s\n", lua_tostring(L, -1));
136            } else {
137                if (lua_isboolean(L, -1) && lua_toboolean(L, -1)) {
138                    this->inval(nullptr);
139                    Click* c = new Click(this);
140                    c->setType(gLuaClickHandlerName);
141                    return c;
142                }
143            }
144        }
145        return this->INHERITED::onFindClickHandler(x, y, modi);
146    }
147
148    bool onClick(Click* click) override {
149        if (click->getType() != gLuaClickHandlerName) {
150            return this->INHERITED::onClick(click);
151        }
152
153        const char* state = nullptr;
154        switch (click->fState) {
155            case Click::kMoved_State:
156                state = "moved";
157                break;
158            case Click::kUp_State:
159                state = "up";
160                break;
161            default:
162                break;
163        }
164        if (state) {
165            this->inval(nullptr);
166            lua_State* L = fLua->get();
167            lua_getglobal(L, gClickName);
168            fLua->pushScalar(click->fCurr.x());
169            fLua->pushScalar(click->fCurr.y());
170            fLua->pushString(state);
171            lua_pcall(L, 3, 1, 0);
172            return lua_isboolean(L, -1) && lua_toboolean(L, -1);
173        }
174        return true;
175    }
176
177private:
178    SkLua* fLua;
179
180    typedef SampleView INHERITED;
181};
182
183//////////////////////////////////////////////////////////////////////////////
184
185static SkView* MyFactory() { return new LuaView; }
186static SkViewRegister reg(MyFactory);
187