Panda3D
Loading...
Searching...
No Matches
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 */
21ModifierButtons::
22ModifierButtons() :
23 _state(0)
24{
25 _button_list = PTA(ButtonHandle)::empty_array(0);
26}
27
28/**
29 *
30 */
31ModifierButtons::
32ModifierButtons(const ModifierButtons &copy) :
33 _button_list(copy._button_list),
34 _state(copy._state)
35{
36}
37
38/**
39 *
40 */
41ModifierButtons::
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 */
50operator &= (const ModifierButtons &other) {
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 */
76operator |= (const ModifierButtons &other) {
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 */
107set_button_list(const ModifierButtons &other) {
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 */
134matches(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 */
180add_button(ButtonHandle button) {
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 */
206has_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 */
275button_up(ButtonHandle button) {
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 */
291is_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 */
306get_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 */
322output(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 */
337write(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 */
356void ModifierButtons::
357modify_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}
A ButtonHandle represents a single button from any device, including keyboard buttons and mouse butto...
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...
This class monitors the state of a number of individual buttons and tracks whether each button is kno...
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...
bool add_button(ButtonHandle button)
Adds the indicated button to the set of buttons that will be monitored for upness and downness.
get_num_buttons
Returns the number of buttons that the ModifierButtons object is monitoring (e.g.
get_button
Returns the nth button that the ModifierButtons object is monitoring (the nth button passed to add_bu...
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...
bool remove_button(ButtonHandle button)
Removes the indicated button from the set of buttons being monitored.
bool button_up(ButtonHandle button)
Records that a particular button has been released.
void operator&=(const ModifierButtons &other)
Sets is_down() true for any button that is already true for this object and the other object.
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 write(std::ostream &out) const
Writes a multi-line summary including all of the buttons being monitored and which ones are known to ...
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...
bool has_button(ButtonHandle button) const
Returns true if the indicated button is in the set of buttons being monitored, false otherwise.
bool button_down(ButtonHandle button)
Records that a particular button has been pressed.
void output(std::ostream &out) const
Writes a one-line summary of the buttons known to be down.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.