v1.1.0
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Modules Pages
public.h
1 /* -*- mode: C; c-basic-offset: 4; intent-tabs-mode: nil -*-
2  *
3  * Sifteo SDK
4  *
5  * Copyright <c> 2012 Sifteo, Inc.
6  *
7  * Permission is hereby granted, free of charge, to any person obtaining a copy
8  * of this software and associated documentation files (the "Software"), to deal
9  * in the Software without restriction, including without limitation the rights
10  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11  * copies of the Software, and to permit persons to whom the Software is
12  * furnished to do so, subject to the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be included in
15  * all copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23  * THE SOFTWARE.
24  */
25 
26 #pragma once
27 #ifdef NOT_USERSPACE
28 # error This is a userspace-only header, not allowed by the current build.
29 #endif
30 
31 #include <sifteo/menu/types.h>
32 
33 namespace Sifteo {
34 
40 inline Menu::Menu(VideoBuffer &vid, const MenuAssets *aAssets, MenuItem *aItems)
41 {
42  init(vid, aAssets, aItems);
43 }
44 
45 inline void Menu::init(VideoBuffer &vid, const MenuAssets *aAssets, MenuItem *aItems)
46 {
47  this->vid = &vid;
48  hasBeenStarted = false;
49 
50  currentEvent.type = MENU_UNEVENTFUL;
51  items = aItems;
52  assets = aAssets;
53  changeState(MENU_STATE_START);
54 
55  // initialize instance constants
56  kHeaderHeight = 0;
57  kFooterHeight = 0;
58  kIconTileWidth = 0;
59 
60  // calculate the number of items
61  uint8_t i = 0;
62  while (items[i].icon != NULL) {
63  if (kIconTileWidth == 0) {
64  kIconTileWidth = items[i].icon->tileWidth();
65  kIconTileHeight = items[i].icon->tileHeight();
66  kEndCapPadding = (kNumVisibleTilesX - kIconTileWidth) * (TILE / 2.f);
67  ASSERT((kItemPixelWidth() - kIconPixelWidth()) % TILE == 0);
68  } else {
69  ASSERT(items[i].icon->tileWidth() == kIconTileWidth);
70  ASSERT(items[i].icon->tileHeight() == kIconTileHeight);
71  }
72 
73  i++;
74  if (items[i].label != NULL) {
75  ASSERT(items[i].label->tileWidth() == kNumVisibleTilesX);
76  if (kHeaderHeight == 0) {
77  kHeaderHeight = items[i].label->tileHeight();
78  } else {
79  ASSERT(items[i].label->tileHeight() == kHeaderHeight);
80  }
81  /* XXX: if there are any labels, a header is required for now.
82  * supporting labels and no header would require toggling bg0
83  * tiles fairly often and that's state I don't want to deal with
84  * for the time being.
85  * workaround: header can be an appropriately-sized, entirely
86  * transparent PNG.
87  */
88  ASSERT(assets->header != NULL);
89  }
90  }
91  numItems = i;
92 
93  // calculate the number of tips
94  i = 0;
95  while (assets->tips[i] != NULL) {
96  ASSERT(assets->tips[i]->tileWidth() == kNumVisibleTilesX);
97  if (kFooterHeight == 0) {
98  kFooterHeight = assets->tips[i]->tileHeight();
99  } else {
100  ASSERT(assets->tips[i]->tileHeight() == kFooterHeight);
101  }
102  i++;
103  }
104  numTips = i;
105 
106  // sanity check the rest of the assets
107  ASSERT(assets->background);
108  ASSERT(assets->background->tileWidth() == 1 && assets->background->tileHeight() == 1);
109  if (assets->footer) {
110  ASSERT(assets->footer->tileWidth() == kNumVisibleTilesX);
111  if (kFooterHeight == 0) {
112  kFooterHeight = assets->footer->tileHeight();
113  } else {
114  ASSERT(assets->footer->tileHeight() == kFooterHeight);
115  }
116  }
117 
118  if (assets->header) {
119  ASSERT(assets->header->tileWidth() == kNumVisibleTilesX);
120  if (kHeaderHeight == 0) {
121  kHeaderHeight = assets->header->tileHeight();
122  } else {
123  ASSERT(assets->header->tileHeight() == kHeaderHeight);
124  }
125  }
126 
127  prev_ut = 0;
128  startingItem = 0;
129 
130  position = 0.0f;
131 
132  setIconYOffset(kDefaultIconYOffset);
133  setPeekTiles(kDefaultPeekTiles);
134 }
135 
136 inline VideoBuffer * Menu::videoBuffer() const
137 {
138  return vid;
139 }
140 
141 inline CubeID Menu::cube() const
142 {
143  return vid ? vid->cube() : CubeID();
144 }
145 
146 inline bool Menu::pollEvent(struct MenuEvent *ev)
147 {
148  // Events not handled at this point are discarded
149  ASSERT(currentEvent.type != MENU_PREPAINT);
150  clearEvent();
151 
152  /* state changes can happen in the default event handler which may dispatch
153  * events (like MENU_STATE_STATIC -> MENU_STATE_FINISH dispatches a
154  * MENU_PREPAINT).
155  */
156  if (dispatchEvent(ev)) {
157  return (ev->type != MENU_EXIT);
158  }
159 
160  // keep track of time so if our framerate changes, apparent speed persists
161  frameclock.next();
162 
163  // neighbor changes?
164  if (currentState != MENU_STATE_START) {
165  detectNeighbors();
166  }
167  if (dispatchEvent(ev)) {
168  return (ev->type != MENU_EXIT);
169  }
170 
171  // update commonly-used data
172  const float kAccelScalingFactor = -0.25f;
173  accel = kAccelScalingFactor * vid->virtualAccel().xy();
174 
175  // state changes
176  switch (currentState) {
177  case MENU_STATE_START:
178  transFromStart();
179  break;
180  case MENU_STATE_STATIC:
181  transFromStatic();
182  break;
183  case MENU_STATE_TILTING:
184  transFromTilting();
185  break;
186  case MENU_STATE_INERTIA:
187  transFromInertia();
188  break;
189  case MENU_STATE_FINISH:
190  transFromFinish();
191  break;
192  case MENU_STATE_HOP_UP:
193  transFromHopUp();
194  break;
195  case MENU_STATE_PAN_TARGET:
196  transFromPanTarget();
197  break;
198  }
199  if (dispatchEvent(ev)) {
200  return (ev->type != MENU_EXIT);
201  }
202 
203  // run loop
204  switch (currentState) {
205  case MENU_STATE_START:
206  stateStart();
207  break;
208  case MENU_STATE_STATIC:
209  stateStatic();
210  break;
211  case MENU_STATE_TILTING:
212  stateTilting();
213  break;
214  case MENU_STATE_INERTIA:
215  stateInertia();
216  break;
217  case MENU_STATE_FINISH:
218  stateFinish();
219  break;
220  case MENU_STATE_HOP_UP:
221  stateHopUp();
222  break;
223  case MENU_STATE_PAN_TARGET:
224  statePanTarget();
225  break;
226  }
227  if (dispatchEvent(ev)) {
228  return (ev->type != MENU_EXIT);
229  }
230 
231  // no special events, paint a frame.
232  drawFooter();
233  currentEvent.type = MENU_PREPAINT;
234  dispatchEvent(ev);
235  return true;
236 }
237 
238 inline void Menu::reset()
239 {
240  changeState(MENU_STATE_START);
241 }
242 
243 inline void Menu::replaceIcon(uint8_t item, const AssetImage *icon, const AssetImage *label)
244 {
245  ASSERT(item < numItems);
246 
247  items[item].icon = icon;
248  for (int i = prev_ut; i < prev_ut + kNumTilesX; i++)
249  if (itemVisibleAtCol(item, i))
250  drawColumn(i);
251 
252  if (label) {
253  uint8_t currentItem = computeSelected();
254  items[item].label = label;
255 
256  if (kHeaderHeight && currentState == MENU_STATE_STATIC &&
257  currentItem == item)
258  {
259  const AssetImage& label = items[currentItem].label
260  ? *items[currentItem].label
261  : *assets->header;
262  vid->bg1.image(vec(0,0), label);
263  }
264  }
265 }
266 
267 inline bool Menu::itemVisible(uint8_t item)
268 {
269  ASSERT(item >= 0 && item < numItems);
270 
271  for (int i = MAX(0, prev_ut); i < prev_ut + kNumTilesX; i++) {
272  if (itemVisibleAtCol(item, i)) return true;
273  }
274  return false;
275 }
276 
277 inline void Menu::setIconYOffset(uint8_t px)
278 {
279  ASSERT(px >= 0 || px < kNumTilesX * 8);
280  kIconYOffset = -px;
281  if (hasBeenStarted)
282  updateBG0();
283 }
284 
285 inline void Menu::setPeekTiles(uint8_t numTiles)
286 {
287  ASSERT(numTiles >= 1 || numTiles * 2 < kNumTilesX);
288  kPeekTiles = numTiles;
289  if (hasBeenStarted)
290  updateBG0();
291 }
292 
301 inline void Menu::anchor(uint8_t item, bool hopUp, int8_t panTarget)
302 {
303  ASSERT(item < numItems);
304  startingItem = item;
305  targetItem = panTarget;
306 
307  if (hopUp) {
308  position = stoppingPositionFor(startingItem);
309  prev_ut = computeCurrentTile() + kNumTilesX;
310  updateBG0();
311 
312  changeState(MENU_STATE_HOP_UP);
313  }
314 }
315 
316 inline MenuState Menu::getState()
317 {
318  return currentState;
319 }
320 
321 inline bool Menu::isTilted()
322 {
323  const float robustThreshold = kAccelThresholdOn * 1.3;
324  return (abs(accel.x) >= robustThreshold); // avoids accel. noise
325 }
326 
327 inline bool Menu::isHorizontal()
328 {
329  const float robustThreshold = kAccelThresholdOn * 0.3;
330  return (abs(accel.x) < robustThreshold); // avoids accel. noise
331 }
332 
333 inline bool Menu::isAtEdge()
334 {
335  uint8_t item = computeSelected();
336  return (item == 0 || item == numItems - 1);
337 }
338 
339 inline bool Menu::isTiltingAtEdge()
340 {
341  /*
342  * if we're tilting up against either the beginning or the end of the menu,
343  * there are no more items to navigate to.
344  */
345  uint8_t item = computeSelected();
346  int8_t direction = accel.x > 0 ? 1 : -1;
347 
348  return ((direction < 0 && item == 0) ||
349  (direction > 0 && item == numItems - 1));
350 }
351 
352 inline void Menu::setNumTips(uint8_t nt)
353 {
354  ASSERT(nt >= 0);
355 
356  numTips = nt;
357 }
358 
359 inline int Menu::getCurrentTip()
360 {
361  ASSERT(currentTip < numTips);
362 
363  return currentTip;
364 }
365 
366 
371 }; // namespace Sifteo
372 
#define ASSERT(_x)
Runtime debug assertion.
Definition: macros.h:205
MenuState
Definition: menu/types.h:108
T abs(const T &value)
For any type, return the absolute value.
Definition: math.h:90
#define MAX(a, b)
Definition: macros.h:350
A memory buffer which holds graphics data.
Definition: video.h:145
Definition: array.h:34
#define NULL
Definition: macros.h:356
CubeID cube() const
Get the CubeID that this buffer is currently attached to.
Definition: video.h:181
Vector2< T > vec(T x, T y)
Create a Vector2, from a set of (x,y) coordinates.
Definition: math.h:658