Panda3D
modifierButtons.cxx
Go to the documentation of this file.
1 /**
2  * PANDA 3D SOFTWARE
3  * Copyright (c) Carnegie Mellon University. All rights reserved.
4  *
5  * All use of this software is subject to the terms of the revised BSD
6  * license. You should have received a copy of this license along
7  * with this source code in a file named "LICENSE."
8  *
9  * @file modifierButtons.cxx
10  * @author drose
11  * @date 2000-03-01
12  */
13 
14 #include "modifierButtons.h"
15 
16 #include "pnotify.h"
17 
18 /**
19  *
20  */
21 ModifierButtons::
22 ModifierButtons() :
23  _state(0)
24 {
25  _button_list = PTA(ButtonHandle)::empty_array(0);
26 }
27 
28 /**
29  *
30  */
31 ModifierButtons::
32 ModifierButtons(const ModifierButtons &copy) :
33  _button_list(copy._button_list),
34  _state(copy._state)
35 {
36 }
37 
38 /**
39  *
40  */
41 ModifierButtons::
42 ~ModifierButtons() {
43 }
44 
45 /**
46  * Sets is_down() true for any button that is already true for this object and
47  * the other object.
48  */
51  if (_button_list == other._button_list) {
52  // Trivially easy case: if the button lists are the same, we can do this
53  // using a bitmask operation.
54  _state &= other._state;
55 
56  } else {
57  // More complicated case: if the button lists are different, we have to
58  // iterate through the buttons and compare them case-by-case. This
59  // becomes an n^2 operation, but fortunately there won't be more than a
60  // handful of buttons.
61  int num_buttons = get_num_buttons();
62  for (int i = 0; i < num_buttons; i++) {
63  if (is_down(i) && !other.is_down(get_button(i))) {
64  _state &= ~((BitmaskType)1 << i);
65  }
66  }
67  }
68 }
69 
70 /**
71  * Sets is_down() true for any button that is already true for this object and
72  * the other object. Adds whatever buttons are necessary to the list to make
73  * this so
74  */
77  if (_button_list == other._button_list) {
78  // Trivially easy case: if the button lists are the same, we can do this
79  // using a bitmask operation.
80  _state |= other._state;
81 
82  } else {
83  // More complicated case: if the button lists are different, we have to
84  // iterate through the buttons and compare them case-by-case. This
85  // becomes an n^2 operation, but fortunately there won't be more than a
86  // handful of buttons.
87  int num_buttons = other.get_num_buttons();
88  for (int i = 0; i < num_buttons; i++) {
89  if (other.is_down(i)) {
90  add_button(other.get_button(i));
91  button_down(other.get_button(i));
92  }
93  }
94  }
95 }
96 
97 /**
98  * Sets the list of buttons to watch to be the same as that of the other
99  * ModifierButtons object. This makes the lists pointer equivalent (until one
100  * or the other is later modified).
101  *
102  * This will preserve the state of any button that was on the original list
103  * and is also on the new lists. Any other buttons will get reset to the
104  * default state of "up".
105  */
108  if (_button_list != other._button_list) {
109  if (_state != 0) {
110  // If we have some buttons already down, we have to copy them to the new
111  // state.
112  BitmaskType new_state = 0;
113  int num_buttons = other.get_num_buttons();
114  for (int i = 0; i < num_buttons; i++) {
115  if (is_down(other.get_button(i))) {
116  new_state |= ((BitmaskType)1 << i);
117  }
118  }
119 
120  _state = new_state;
121  }
122 
123  _button_list = other._button_list;
124  }
125 }
126 
127 /**
128  * Returns true if the set of buttons indicated as down by this
129  * ModifierButtons object is the same set of buttons indicated as down by the
130  * other ModifierButtons object. The buttons indicated as up are not
131  * relevant.
132  */
134 matches(const ModifierButtons &other) const {
135  if (_button_list == other._button_list) {
136  // If the two objects share the same array, we only need to check the
137  // bitmask. This is a simple optimization.
138  return (_state == other._state);
139  }
140 
141  // The two objects do not share the same array; thus we have to do this one
142  // button at a time. This is an n-squared operation, but presumably there
143  // will not be hundreds of buttons to compare.
144 
145  // First, check that all the buttons indicated as down in our object are
146  // also indicated as down in the other object.
147  int num_down = 0;
148 
149  int i;
150  for (i = 0; i < (int)_button_list.size(); i++) {
151  if (is_down(i)) {
152  if (!other.is_down(_button_list[i])) {
153  return false;
154  }
155  num_down++;
156  }
157  }
158 
159  // Now make sure the total number of buttons indicated as down in our object
160  // matches the number indicated as down in the other object. This ensures
161  // there aren't any additional buttons indicated down in the other object.
162  int num_other_buttons = other.get_num_buttons();
163  int num_other_down = 0;
164  for (i = 0; i < num_other_buttons; i++) {
165  if (other.is_down(i)) {
166  num_other_down++;
167  }
168  }
169 
170  return (num_down == num_other_down);
171 }
172 
173 /**
174  * Adds the indicated button to the set of buttons that will be monitored for
175  * upness and downness. Returns true if the button was added, false if it was
176  * already being monitored or if too many buttons are currently being
177  * monitored.
178  */
181  nassertr(button != ButtonHandle::none(), false);
182 
183  static const int max_buttons = sizeof(BitmaskType) * 8;
184 
185  if ((int)_button_list.size() >= max_buttons) {
186  return false;
187  }
188 
189  // First, check to see if the button is already being monitored.
190  if (has_button(button)) {
191  return false;
192  }
193 
194  // Ok, it's not; add it.
195  modify_button_list();
196  _button_list.push_back(button);
197 
198  return true;
199 }
200 
201 /**
202  * Returns true if the indicated button is in the set of buttons being
203  * monitored, false otherwise.
204  */
206 has_button(ButtonHandle button) const {
207  PTA(ButtonHandle)::const_iterator bi;
208  for (bi = _button_list.begin(); bi != _button_list.end(); ++bi) {
209  if (button.matches(*bi)) {
210  return true;
211  }
212  }
213 
214  return false;
215 }
216 
217 /**
218  * Removes the indicated button from the set of buttons being monitored.
219  * Returns true if the button was removed, false if it was not being monitored
220  * in the first place.
221  *
222  * Unlike the other methods, you cannot remove a button by removing its alias;
223  * you have to remove exactly the button itself.
224  */
227  // We use i instead of an iterator, because we need to call
228  // modify_button_list() just before we remove the button, and that may
229  // invalidate all of the iterators.
230 
231  for (int i = 0; i < (int)_button_list.size(); i++) {
232  if (button == _button_list[i]) {
233  modify_button_list();
234  _button_list.erase(_button_list.begin() + i);
235 
236  // Now remove the corresponding bit from the bitmask and shift all the
237  // bits above it down.
238  BitmaskType mask = ((BitmaskType)1 << i);
239  BitmaskType below = mask - 1;
240  BitmaskType above = (~below) & (~mask);
241 
242  _state = ((_state & above) >> 1) | (_state & below);
243  return true;
244  }
245  }
246 
247  return false;
248 }
249 
250 /**
251  * Records that a particular button has been pressed. If the given button is
252  * one of the buttons that is currently being monitored, this will update the
253  * internal state appropriately; otherwise, it will do nothing. Returns true
254  * if the button is one that was monitored, or false otherwise.
255  */
258  for (int i = 0; i < (int)_button_list.size(); i++) {
259  if (button.matches(_button_list[i])) {
260  _state |= ((BitmaskType)1 << i);
261  return true;
262  }
263  }
264 
265  return false;
266 }
267 
268 /**
269  * Records that a particular button has been released. If the given button is
270  * one of the buttons that is currently being monitored, this will update the
271  * internal state appropriately; otherwise, it will do nothing. Returns true
272  * if the button is one that was monitored, or false otherwise.
273  */
276  for (int i = 0; i < (int)_button_list.size(); i++) {
277  if (button.matches(_button_list[i])) {
278  _state &= ~((BitmaskType)1 << i);
279  return true;
280  }
281  }
282 
283  return false;
284 }
285 
286 /**
287  * Returns true if the indicated button is known to be down, or false if it is
288  * known to be up or if it is not in the set of buttons being tracked.
289  */
291 is_down(ButtonHandle button) const {
292  for (int i = 0; i < (int)_button_list.size(); i++) {
293  if (button.matches(_button_list[i])) {
294  return ((_state & ((BitmaskType)1 << i)) != 0);
295  }
296  }
297 
298  return false;
299 }
300 
301 /**
302  * Returns a string which can be used to prefix any button name or event name
303  * with the unique set of modifier buttons currently being held.
304  */
305 std::string ModifierButtons::
306 get_prefix() const {
307  std::string prefix;
308  for (int i = 0; i < (int)_button_list.size(); i++) {
309  if ((_state & ((BitmaskType)1 << i)) != 0) {
310  prefix += _button_list[i].get_name();
311  prefix += '-';
312  }
313  }
314 
315  return prefix;
316 }
317 
318 /**
319  * Writes a one-line summary of the buttons known to be down.
320  */
322 output(std::ostream &out) const {
323  out << "[";
324  for (int i = 0; i < (int)_button_list.size(); i++) {
325  if ((_state & ((BitmaskType)1 << i)) != 0) {
326  out << " " << _button_list[i];
327  }
328  }
329  out << " ]";
330 }
331 
332 /**
333  * Writes a multi-line summary including all of the buttons being monitored
334  * and which ones are known to be down.
335  */
337 write(std::ostream &out) const {
338  out << "ModifierButtons:\n";
339  for (int i = 0; i < (int)_button_list.size(); i++) {
340  out << " " << _button_list[i];
341  if ((_state & ((BitmaskType)1 << i)) != 0) {
342  out << " (down)";
343  }
344  out << "\n";
345  }
346 }
347 
348 /**
349  * Implements a poor-man's copy-on-write for the ModifierButtons class. If
350  * any reference counts are held on our _button_list, besides ourselves, then
351  * allocates and copies a brand new copy of the _button_list. This should be
352  * done in preparation for any modifications to the _button_list, since
353  * multiple instances of the ModifierButtons object may share the same
354  * _button_list pointer.
355  */
356 void ModifierButtons::
357 modify_button_list() {
358  if (_button_list.get_ref_count() > 1) {
359  PTA(ButtonHandle) old_list = _button_list;
360 
361  _button_list = PTA(ButtonHandle)::empty_array(0);
362 
363  // This forces a new allocation and memberwise copy, instead of just a
364  // reference-counting pointer copy.
365  _button_list.v() = old_list.v();
366  }
367 
368  // Now we should be the only ones holding a count.
369  nassertv(_button_list.get_ref_count() == 1);
370 }
bool button_down(ButtonHandle button)
Records that a particular button has been pressed.
void write(std::ostream &out) const
Writes a multi-line summary including all of the buttons being monitored and which ones are known to ...
This class monitors the state of a number of individual buttons and tracks whether each button is kno...
bool remove_button(ButtonHandle button)
Removes the indicated button from the set of buttons being monitored.
std::string get_prefix() const
Returns a string which can be used to prefix any button name or event name with the unique set of mod...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
bool has_button(ButtonHandle button) const
Returns true if the indicated button is in the set of buttons being monitored, false otherwise.
get_button
Returns the nth button that the ModifierButtons object is monitoring (the nth button passed to add_bu...
bool matches(const ModifierButtons &other) const
Returns true if the set of buttons indicated as down by this ModifierButtons object is the same set o...
get_num_buttons
Returns the number of buttons that the ModifierButtons object is monitoring (e.g.
bool button_up(ButtonHandle button)
Records that a particular button has been released.
bool is_down(ButtonHandle button) const
Returns true if the indicated button is known to be down, or false if it is known to be up or if it i...
A ButtonHandle represents a single button from any device, including keyboard buttons and mouse butto...
Definition: buttonHandle.h:26
void output(std::ostream &out) const
Writes a one-line summary of the buttons known to be down.
bool matches(const ButtonHandle &other) const
Returns true if this ButtonHandle is the same as the other one, or if the other one is an alias for t...
Definition: buttonHandle.I:114
bool add_button(ButtonHandle button)
Adds the indicated button to the set of buttons that will be monitored for upness and downness.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void set_button_list(const ModifierButtons &other)
Sets the list of buttons to watch to be the same as that of the other ModifierButtons object.
void operator &=(const ModifierButtons &other)
Sets is_down() true for any button that is already true for this object and the other object.
void operator|=(const ModifierButtons &other)
Sets is_down() true for any button that is already true for this object and the other object.