/indra/newview/llviewerkeyboard.cpp
C++ | 978 lines | 765 code | 129 blank | 84 comment | 195 complexity | be892abf29f35a37cb161c53dff39fd8 MD5 | raw file
Possible License(s): LGPL-2.1
1/**
2 * @file llviewerkeyboard.cpp
3 * @brief LLViewerKeyboard class implementation
4 *
5 * $LicenseInfo:firstyear=2005&license=viewerlgpl$
6 * Second Life Viewer Source Code
7 * Copyright (C) 2010, Linden Research, Inc.
8 *
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation;
12 * version 2.1 of the License only.
13 *
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22 *
23 * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
24 * $/LicenseInfo$
25 */
26
27#include "llviewerprecompiledheaders.h"
28
29#include "llappviewer.h"
30#include "llviewerkeyboard.h"
31#include "llmath.h"
32#include "llagent.h"
33#include "llagentcamera.h"
34#include "llnearbychatbar.h"
35#include "llviewercontrol.h"
36#include "llfocusmgr.h"
37#include "llmorphview.h"
38#include "llmoveview.h"
39#include "lltoolfocus.h"
40#include "llviewerwindow.h"
41#include "llvoavatarself.h"
42#include "llfloatercamera.h"
43#include "llinitparam.h"
44
45//
46// Constants
47//
48
49const F32 FLY_TIME = 0.5f;
50const F32 FLY_FRAMES = 4;
51
52const F32 NUDGE_TIME = 0.25f; // in seconds
53const S32 NUDGE_FRAMES = 2;
54const F32 ORBIT_NUDGE_RATE = 0.05f; // fraction of normal speed
55const F32 YAW_NUDGE_RATE = 0.05f; // fraction of normal speed
56
57struct LLKeyboardActionRegistry
58: public LLRegistrySingleton<std::string, boost::function<void (EKeystate keystate)>, LLKeyboardActionRegistry>
59{
60};
61
62LLViewerKeyboard gViewerKeyboard;
63
64void agent_jump( EKeystate s )
65{
66 if( KEYSTATE_UP == s ) return;
67 F32 time = gKeyboard->getCurKeyElapsedTime();
68 S32 frame_count = llround(gKeyboard->getCurKeyElapsedFrameCount());
69
70 if( time < FLY_TIME
71 || frame_count <= FLY_FRAMES
72 || gAgent.upGrabbed()
73 || !gSavedSettings.getBOOL("AutomaticFly"))
74 {
75 gAgent.moveUp(1);
76 }
77 else
78 {
79 gAgent.setFlying(TRUE);
80 gAgent.moveUp(1);
81 }
82}
83
84void agent_push_down( EKeystate s )
85{
86 if( KEYSTATE_UP == s ) return;
87 gAgent.moveUp(-1);
88}
89
90static void agent_handle_doubletap_run(EKeystate s, LLAgent::EDoubleTapRunMode mode)
91{
92 if (KEYSTATE_UP == s)
93 {
94 if (gAgent.mDoubleTapRunMode == mode &&
95 gAgent.getRunning() &&
96 !gAgent.getAlwaysRun())
97 {
98 // Turn off temporary running.
99 gAgent.clearRunning();
100 gAgent.sendWalkRun(gAgent.getRunning());
101 }
102 }
103 else if (gSavedSettings.getBOOL("AllowTapTapHoldRun") &&
104 KEYSTATE_DOWN == s &&
105 !gAgent.getRunning())
106 {
107 if (gAgent.mDoubleTapRunMode == mode &&
108 gAgent.mDoubleTapRunTimer.getElapsedTimeF32() < NUDGE_TIME)
109 {
110 // Same walk-key was pushed again quickly; this is a
111 // double-tap so engage temporary running.
112 gAgent.setRunning();
113 gAgent.sendWalkRun(gAgent.getRunning());
114 }
115
116 // Pressing any walk-key resets the double-tap timer
117 gAgent.mDoubleTapRunTimer.reset();
118 gAgent.mDoubleTapRunMode = mode;
119 }
120}
121
122static void agent_push_forwardbackward( EKeystate s, S32 direction, LLAgent::EDoubleTapRunMode mode )
123{
124 agent_handle_doubletap_run(s, mode);
125 if (KEYSTATE_UP == s) return;
126
127 F32 time = gKeyboard->getCurKeyElapsedTime();
128 S32 frame_count = llround(gKeyboard->getCurKeyElapsedFrameCount());
129
130 if( time < NUDGE_TIME || frame_count <= NUDGE_FRAMES)
131 {
132 gAgent.moveAtNudge(direction);
133 }
134 else
135 {
136 gAgent.moveAt(direction);
137 }
138}
139
140void camera_move_forward( EKeystate s );
141
142void agent_push_forward( EKeystate s )
143{
144 //in free camera control mode we need to intercept keyboard events for avatar movements
145 if (LLFloaterCamera::inFreeCameraMode())
146 {
147 camera_move_forward(s);
148 return;
149 }
150 agent_push_forwardbackward(s, 1, LLAgent::DOUBLETAP_FORWARD);
151}
152
153void camera_move_backward( EKeystate s );
154
155void agent_push_backward( EKeystate s )
156{
157 //in free camera control mode we need to intercept keyboard events for avatar movements
158 if (LLFloaterCamera::inFreeCameraMode())
159 {
160 camera_move_backward(s);
161 return;
162 }
163 agent_push_forwardbackward(s, -1, LLAgent::DOUBLETAP_BACKWARD);
164}
165
166static void agent_slide_leftright( EKeystate s, S32 direction, LLAgent::EDoubleTapRunMode mode )
167{
168 agent_handle_doubletap_run(s, mode);
169 if( KEYSTATE_UP == s ) return;
170 F32 time = gKeyboard->getCurKeyElapsedTime();
171 S32 frame_count = llround(gKeyboard->getCurKeyElapsedFrameCount());
172
173 if( time < NUDGE_TIME || frame_count <= NUDGE_FRAMES)
174 {
175 gAgent.moveLeftNudge(direction);
176 }
177 else
178 {
179 gAgent.moveLeft(direction);
180 }
181}
182
183
184void agent_slide_left( EKeystate s )
185{
186 agent_slide_leftright(s, 1, LLAgent::DOUBLETAP_SLIDELEFT);
187}
188
189
190void agent_slide_right( EKeystate s )
191{
192 agent_slide_leftright(s, -1, LLAgent::DOUBLETAP_SLIDERIGHT);
193}
194
195void camera_spin_around_cw( EKeystate s );
196
197void agent_turn_left( EKeystate s )
198{
199 //in free camera control mode we need to intercept keyboard events for avatar movements
200 if (LLFloaterCamera::inFreeCameraMode())
201 {
202 camera_spin_around_cw(s);
203 return;
204 }
205
206 if (LLToolCamera::getInstance()->mouseSteerMode())
207 {
208 agent_slide_left(s);
209 }
210 else
211 {
212 if (KEYSTATE_UP == s) return;
213 F32 time = gKeyboard->getCurKeyElapsedTime();
214 gAgent.moveYaw( LLFloaterMove::getYawRate( time ) );
215 }
216}
217
218void camera_spin_around_ccw( EKeystate s );
219
220void agent_turn_right( EKeystate s )
221{
222 //in free camera control mode we need to intercept keyboard events for avatar movements
223 if (LLFloaterCamera::inFreeCameraMode())
224 {
225 camera_spin_around_ccw(s);
226 return;
227 }
228
229 if (LLToolCamera::getInstance()->mouseSteerMode())
230 {
231 agent_slide_right(s);
232 }
233 else
234 {
235 if (KEYSTATE_UP == s) return;
236 F32 time = gKeyboard->getCurKeyElapsedTime();
237 gAgent.moveYaw( -LLFloaterMove::getYawRate( time ) );
238 }
239}
240
241void agent_look_up( EKeystate s )
242{
243 if( KEYSTATE_UP == s ) return;
244 gAgent.movePitch(-1);
245 //gAgent.rotate(-2.f * DEG_TO_RAD, gAgent.getFrame().getLeftAxis() );
246}
247
248
249void agent_look_down( EKeystate s )
250{
251 if( KEYSTATE_UP == s ) return;
252 gAgent.movePitch(1);
253 //gAgent.rotate(2.f * DEG_TO_RAD, gAgent.getFrame().getLeftAxis() );
254}
255
256void agent_toggle_fly( EKeystate s )
257{
258 // Only catch the edge
259 if (KEYSTATE_DOWN == s )
260 {
261 LLAgent::toggleFlying();
262 }
263}
264
265F32 get_orbit_rate()
266{
267 F32 time = gKeyboard->getCurKeyElapsedTime();
268 if( time < NUDGE_TIME )
269 {
270 F32 rate = ORBIT_NUDGE_RATE + time * (1 - ORBIT_NUDGE_RATE)/ NUDGE_TIME;
271 //llinfos << rate << llendl;
272 return rate;
273 }
274 else
275 {
276 return 1;
277 }
278}
279
280void camera_spin_around_ccw( EKeystate s )
281{
282 if( KEYSTATE_UP == s ) return;
283 gAgentCamera.unlockView();
284 gAgentCamera.setOrbitLeftKey( get_orbit_rate() );
285}
286
287
288void camera_spin_around_cw( EKeystate s )
289{
290 if( KEYSTATE_UP == s ) return;
291 gAgentCamera.unlockView();
292 gAgentCamera.setOrbitRightKey( get_orbit_rate() );
293}
294
295void camera_spin_around_ccw_sitting( EKeystate s )
296{
297 if( KEYSTATE_UP == s ) return;
298 if (gAgent.rotateGrabbed() || gAgentCamera.sitCameraEnabled())
299 {
300 //send keystrokes, but do not change camera
301 agent_turn_right(s);
302 }
303 else
304 {
305 //change camera but do not send keystrokes
306 gAgentCamera.setOrbitLeftKey( get_orbit_rate() );
307 }
308}
309
310
311void camera_spin_around_cw_sitting( EKeystate s )
312{
313 if( KEYSTATE_UP == s ) return;
314 if (gAgent.rotateGrabbed() || gAgentCamera.sitCameraEnabled())
315 {
316 //send keystrokes, but do not change camera
317 agent_turn_left(s);
318 }
319 else
320 {
321 //change camera but do not send keystrokes
322 gAgentCamera.setOrbitRightKey( get_orbit_rate() );
323 }
324}
325
326
327void camera_spin_over( EKeystate s )
328{
329 if( KEYSTATE_UP == s ) return;
330 gAgentCamera.unlockView();
331 gAgentCamera.setOrbitUpKey( get_orbit_rate() );
332}
333
334
335void camera_spin_under( EKeystate s )
336{
337 if( KEYSTATE_UP == s ) return;
338 gAgentCamera.unlockView();
339 gAgentCamera.setOrbitDownKey( get_orbit_rate() );
340}
341
342void camera_spin_over_sitting( EKeystate s )
343{
344 if( KEYSTATE_UP == s ) return;
345 if (gAgent.upGrabbed() || gAgentCamera.sitCameraEnabled())
346 {
347 //send keystrokes, but do not change camera
348 agent_jump(s);
349 }
350 else
351 {
352 //change camera but do not send keystrokes
353 gAgentCamera.setOrbitUpKey( get_orbit_rate() );
354 }
355}
356
357
358void camera_spin_under_sitting( EKeystate s )
359{
360 if( KEYSTATE_UP == s ) return;
361 if (gAgent.downGrabbed() || gAgentCamera.sitCameraEnabled())
362 {
363 //send keystrokes, but do not change camera
364 agent_push_down(s);
365 }
366 else
367 {
368 //change camera but do not send keystrokes
369 gAgentCamera.setOrbitDownKey( get_orbit_rate() );
370 }
371}
372
373void camera_move_forward( EKeystate s )
374{
375 if( KEYSTATE_UP == s ) return;
376 gAgentCamera.unlockView();
377 gAgentCamera.setOrbitInKey( get_orbit_rate() );
378}
379
380
381void camera_move_backward( EKeystate s )
382{
383 if( KEYSTATE_UP == s ) return;
384 gAgentCamera.unlockView();
385 gAgentCamera.setOrbitOutKey( get_orbit_rate() );
386}
387
388void camera_move_forward_sitting( EKeystate s )
389{
390 if( KEYSTATE_UP == s ) return;
391 if (gAgent.forwardGrabbed() || gAgentCamera.sitCameraEnabled())
392 {
393 agent_push_forward(s);
394 }
395 else
396 {
397 gAgentCamera.setOrbitInKey( get_orbit_rate() );
398 }
399}
400
401
402void camera_move_backward_sitting( EKeystate s )
403{
404 if( KEYSTATE_UP == s ) return;
405
406 if (gAgent.backwardGrabbed() || gAgentCamera.sitCameraEnabled())
407 {
408 agent_push_backward(s);
409 }
410 else
411 {
412 gAgentCamera.setOrbitOutKey( get_orbit_rate() );
413 }
414}
415
416void camera_pan_up( EKeystate s )
417{
418 if( KEYSTATE_UP == s ) return;
419 gAgentCamera.unlockView();
420 gAgentCamera.setPanUpKey( get_orbit_rate() );
421}
422
423void camera_pan_down( EKeystate s )
424{
425 if( KEYSTATE_UP == s ) return;
426 gAgentCamera.unlockView();
427 gAgentCamera.setPanDownKey( get_orbit_rate() );
428}
429
430void camera_pan_left( EKeystate s )
431{
432 if( KEYSTATE_UP == s ) return;
433 gAgentCamera.unlockView();
434 gAgentCamera.setPanLeftKey( get_orbit_rate() );
435}
436
437void camera_pan_right( EKeystate s )
438{
439 if( KEYSTATE_UP == s ) return;
440 gAgentCamera.unlockView();
441 gAgentCamera.setPanRightKey( get_orbit_rate() );
442}
443
444void camera_pan_in( EKeystate s )
445{
446 if( KEYSTATE_UP == s ) return;
447 gAgentCamera.unlockView();
448 gAgentCamera.setPanInKey( get_orbit_rate() );
449}
450
451void camera_pan_out( EKeystate s )
452{
453 if( KEYSTATE_UP == s ) return;
454 gAgentCamera.unlockView();
455 gAgentCamera.setPanOutKey( get_orbit_rate() );
456}
457
458void camera_move_forward_fast( EKeystate s )
459{
460 if( KEYSTATE_UP == s ) return;
461 gAgentCamera.unlockView();
462 gAgentCamera.setOrbitInKey(2.5f);
463}
464
465void camera_move_backward_fast( EKeystate s )
466{
467 if( KEYSTATE_UP == s ) return;
468 gAgentCamera.unlockView();
469 gAgentCamera.setOrbitOutKey(2.5f);
470}
471
472
473void edit_avatar_spin_ccw( EKeystate s )
474{
475 if( KEYSTATE_UP == s ) return;
476 gMorphView->setCameraDrivenByKeys( TRUE );
477 gAgentCamera.setOrbitLeftKey( get_orbit_rate() );
478 //gMorphView->orbitLeft( get_orbit_rate() );
479}
480
481
482void edit_avatar_spin_cw( EKeystate s )
483{
484 if( KEYSTATE_UP == s ) return;
485 gMorphView->setCameraDrivenByKeys( TRUE );
486 gAgentCamera.setOrbitRightKey( get_orbit_rate() );
487 //gMorphView->orbitRight( get_orbit_rate() );
488}
489
490void edit_avatar_spin_over( EKeystate s )
491{
492 if( KEYSTATE_UP == s ) return;
493 gMorphView->setCameraDrivenByKeys( TRUE );
494 gAgentCamera.setOrbitUpKey( get_orbit_rate() );
495 //gMorphView->orbitUp( get_orbit_rate() );
496}
497
498
499void edit_avatar_spin_under( EKeystate s )
500{
501 if( KEYSTATE_UP == s ) return;
502 gMorphView->setCameraDrivenByKeys( TRUE );
503 gAgentCamera.setOrbitDownKey( get_orbit_rate() );
504 //gMorphView->orbitDown( get_orbit_rate() );
505}
506
507void edit_avatar_move_forward( EKeystate s )
508{
509 if( KEYSTATE_UP == s ) return;
510 gMorphView->setCameraDrivenByKeys( TRUE );
511 gAgentCamera.setOrbitInKey( get_orbit_rate() );
512 //gMorphView->orbitIn();
513}
514
515
516void edit_avatar_move_backward( EKeystate s )
517{
518 if( KEYSTATE_UP == s ) return;
519 gMorphView->setCameraDrivenByKeys( TRUE );
520 gAgentCamera.setOrbitOutKey( get_orbit_rate() );
521 //gMorphView->orbitOut();
522}
523
524void stop_moving( EKeystate s )
525{
526 if( KEYSTATE_UP == s ) return;
527 // stop agent
528 gAgent.setControlFlags(AGENT_CONTROL_STOP);
529
530 // cancel autopilot
531 gAgent.stopAutoPilot();
532}
533
534void start_chat( EKeystate s )
535{
536 // start chat
537 LLNearbyChatBar::startChat(NULL);
538}
539
540void start_gesture( EKeystate s )
541{
542 LLUICtrl* focus_ctrlp = dynamic_cast<LLUICtrl*>(gFocusMgr.getKeyboardFocus());
543 if (KEYSTATE_UP == s &&
544 ! (focus_ctrlp && focus_ctrlp->acceptsTextInput()))
545 {
546 if (LLNearbyChatBar::getInstance()->getCurrentChat().empty())
547 {
548 // No existing chat in chat editor, insert '/'
549 LLNearbyChatBar::startChat("/");
550 }
551 else
552 {
553 // Don't overwrite existing text in chat editor
554 LLNearbyChatBar::startChat(NULL);
555 }
556 }
557}
558
559#define REGISTER_KEYBOARD_ACTION(KEY, ACTION) LLREGISTER_STATIC(LLKeyboardActionRegistry, KEY, ACTION);
560REGISTER_KEYBOARD_ACTION("jump", agent_jump);
561REGISTER_KEYBOARD_ACTION("push_down", agent_push_down);
562REGISTER_KEYBOARD_ACTION("push_forward", agent_push_forward);
563REGISTER_KEYBOARD_ACTION("push_backward", agent_push_backward);
564REGISTER_KEYBOARD_ACTION("look_up", agent_look_up);
565REGISTER_KEYBOARD_ACTION("look_down", agent_look_down);
566REGISTER_KEYBOARD_ACTION("toggle_fly", agent_toggle_fly);
567REGISTER_KEYBOARD_ACTION("turn_left", agent_turn_left);
568REGISTER_KEYBOARD_ACTION("turn_right", agent_turn_right);
569REGISTER_KEYBOARD_ACTION("slide_left", agent_slide_left);
570REGISTER_KEYBOARD_ACTION("slide_right", agent_slide_right);
571REGISTER_KEYBOARD_ACTION("spin_around_ccw", camera_spin_around_ccw);
572REGISTER_KEYBOARD_ACTION("spin_around_cw", camera_spin_around_cw);
573REGISTER_KEYBOARD_ACTION("spin_around_ccw_sitting", camera_spin_around_ccw_sitting);
574REGISTER_KEYBOARD_ACTION("spin_around_cw_sitting", camera_spin_around_cw_sitting);
575REGISTER_KEYBOARD_ACTION("spin_over", camera_spin_over);
576REGISTER_KEYBOARD_ACTION("spin_under", camera_spin_under);
577REGISTER_KEYBOARD_ACTION("spin_over_sitting", camera_spin_over_sitting);
578REGISTER_KEYBOARD_ACTION("spin_under_sitting", camera_spin_under_sitting);
579REGISTER_KEYBOARD_ACTION("move_forward", camera_move_forward);
580REGISTER_KEYBOARD_ACTION("move_backward", camera_move_backward);
581REGISTER_KEYBOARD_ACTION("move_forward_sitting", camera_move_forward_sitting);
582REGISTER_KEYBOARD_ACTION("move_backward_sitting", camera_move_backward_sitting);
583REGISTER_KEYBOARD_ACTION("pan_up", camera_pan_up);
584REGISTER_KEYBOARD_ACTION("pan_down", camera_pan_down);
585REGISTER_KEYBOARD_ACTION("pan_left", camera_pan_left);
586REGISTER_KEYBOARD_ACTION("pan_right", camera_pan_right);
587REGISTER_KEYBOARD_ACTION("pan_in", camera_pan_in);
588REGISTER_KEYBOARD_ACTION("pan_out", camera_pan_out);
589REGISTER_KEYBOARD_ACTION("move_forward_fast", camera_move_forward_fast);
590REGISTER_KEYBOARD_ACTION("move_backward_fast", camera_move_backward_fast);
591REGISTER_KEYBOARD_ACTION("edit_avatar_spin_ccw", edit_avatar_spin_ccw);
592REGISTER_KEYBOARD_ACTION("edit_avatar_spin_cw", edit_avatar_spin_cw);
593REGISTER_KEYBOARD_ACTION("edit_avatar_spin_over", edit_avatar_spin_over);
594REGISTER_KEYBOARD_ACTION("edit_avatar_spin_under", edit_avatar_spin_under);
595REGISTER_KEYBOARD_ACTION("edit_avatar_move_forward", edit_avatar_move_forward);
596REGISTER_KEYBOARD_ACTION("edit_avatar_move_backward", edit_avatar_move_backward);
597REGISTER_KEYBOARD_ACTION("stop_moving", stop_moving);
598REGISTER_KEYBOARD_ACTION("start_chat", start_chat);
599REGISTER_KEYBOARD_ACTION("start_gesture", start_gesture);
600#undef REGISTER_KEYBOARD_ACTION
601
602LLViewerKeyboard::LLViewerKeyboard()
603{
604 for (S32 i = 0; i < MODE_COUNT; i++)
605 {
606 mBindingCount[i] = 0;
607 }
608
609 for (S32 i = 0; i < KEY_COUNT; i++)
610 {
611 mKeyHandledByUI[i] = FALSE;
612 }
613 // we want the UI to never see these keys so that they can always control the avatar/camera
614 for(KEY k = KEY_PAD_UP; k <= KEY_PAD_DIVIDE; k++)
615 {
616 mKeysSkippedByUI.insert(k);
617 }
618}
619
620BOOL LLViewerKeyboard::modeFromString(const std::string& string, S32 *mode)
621{
622 if (string == "FIRST_PERSON")
623 {
624 *mode = MODE_FIRST_PERSON;
625 return TRUE;
626 }
627 else if (string == "THIRD_PERSON")
628 {
629 *mode = MODE_THIRD_PERSON;
630 return TRUE;
631 }
632 else if (string == "EDIT")
633 {
634 *mode = MODE_EDIT;
635 return TRUE;
636 }
637 else if (string == "EDIT_AVATAR")
638 {
639 *mode = MODE_EDIT_AVATAR;
640 return TRUE;
641 }
642 else if (string == "SITTING")
643 {
644 *mode = MODE_SITTING;
645 return TRUE;
646 }
647 else
648 {
649 *mode = MODE_THIRD_PERSON;
650 return FALSE;
651 }
652}
653
654BOOL LLViewerKeyboard::handleKey(KEY translated_key, MASK translated_mask, BOOL repeated)
655{
656 // check for re-map
657 EKeyboardMode mode = gViewerKeyboard.getMode();
658 U32 keyidx = (translated_mask<<16) | translated_key;
659 key_remap_t::iterator iter = mRemapKeys[mode].find(keyidx);
660 if (iter != mRemapKeys[mode].end())
661 {
662 translated_key = (iter->second) & 0xff;
663 translated_mask = (iter->second)>>16;
664 }
665
666 // No repeats of F-keys
667 BOOL repeatable_key = (translated_key < KEY_F1 || translated_key > KEY_F12);
668 if (!repeatable_key && repeated)
669 {
670 return FALSE;
671 }
672
673 lldebugst(LLERR_USER_INPUT) << "keydown -" << translated_key << "-" << llendl;
674 // skip skipped keys
675 if(mKeysSkippedByUI.find(translated_key) != mKeysSkippedByUI.end())
676 {
677 mKeyHandledByUI[translated_key] = FALSE;
678 }
679 else
680 {
681 // it is sufficient to set this value once per call to handlekey
682 // without clearing it, as it is only used in the subsequent call to scanKey
683 mKeyHandledByUI[translated_key] = gViewerWindow->handleKey(translated_key, translated_mask);
684 }
685 return mKeyHandledByUI[translated_key];
686}
687
688
689
690BOOL LLViewerKeyboard::bindKey(const S32 mode, const KEY key, const MASK mask, const std::string& function_name)
691{
692 S32 index;
693 typedef boost::function<void(EKeystate)> function_t;
694 function_t function = NULL;
695 std::string name;
696
697 // Allow remapping of F2-F12
698 if (function_name[0] == 'F')
699 {
700 int c1 = function_name[1] - '0';
701 int c2 = function_name[2] ? function_name[2] - '0' : -1;
702 if (c1 >= 0 && c1 <= 9 && c2 >= -1 && c2 <= 9)
703 {
704 int idx = c1;
705 if (c2 >= 0)
706 idx = idx*10 + c2;
707 if (idx >=2 && idx <= 12)
708 {
709 U32 keyidx = ((mask<<16)|key);
710 (mRemapKeys[mode])[keyidx] = ((0<<16)|(KEY_F1+(idx-1)));
711 return TRUE;
712 }
713 }
714 }
715
716 // Not remapped, look for a function
717
718 function_t* result = LLKeyboardActionRegistry::getValue(function_name);
719 if (result)
720 {
721 function = *result;
722 }
723
724 if (!function)
725 {
726 llerrs << "Can't bind key to function " << function_name << ", no function with this name found" << llendl;
727 return FALSE;
728 }
729
730 // check for duplicate first and overwrite
731 for (index = 0; index < mBindingCount[mode]; index++)
732 {
733 if (key == mBindings[mode][index].mKey && mask == mBindings[mode][index].mMask)
734 break;
735 }
736
737 if (index >= MAX_KEY_BINDINGS)
738 {
739 llerrs << "LLKeyboard::bindKey() - too many keys for mode " << mode << llendl;
740 return FALSE;
741 }
742
743 if (mode >= MODE_COUNT)
744 {
745 llerror("LLKeyboard::bindKey() - unknown mode passed", mode);
746 return FALSE;
747 }
748
749 mBindings[mode][index].mKey = key;
750 mBindings[mode][index].mMask = mask;
751 mBindings[mode][index].mFunction = function;
752
753 if (index == mBindingCount[mode])
754 mBindingCount[mode]++;
755
756 return TRUE;
757}
758
759LLViewerKeyboard::KeyBinding::KeyBinding()
760: key("key"),
761 mask("mask"),
762 command("command")
763{}
764
765LLViewerKeyboard::KeyMode::KeyMode(EKeyboardMode _mode)
766: bindings("binding"),
767 mode(_mode)
768{}
769
770LLViewerKeyboard::Keys::Keys()
771: first_person("first_person", KeyMode(MODE_FIRST_PERSON)),
772 third_person("third_person", KeyMode(MODE_THIRD_PERSON)),
773 edit("edit", KeyMode(MODE_EDIT)),
774 sitting("sitting", KeyMode(MODE_SITTING)),
775 edit_avatar("edit_avatar", KeyMode(MODE_EDIT_AVATAR))
776{}
777
778S32 LLViewerKeyboard::loadBindingsXML(const std::string& filename)
779{
780 S32 binding_count = 0;
781 Keys keys;
782 LLSimpleXUIParser parser;
783
784 if (parser.readXUI(filename, keys)
785 && keys.validateBlock())
786 {
787 binding_count += loadBindingMode(keys.first_person);
788 binding_count += loadBindingMode(keys.third_person);
789 binding_count += loadBindingMode(keys.edit);
790 binding_count += loadBindingMode(keys.sitting);
791 binding_count += loadBindingMode(keys.edit_avatar);
792 }
793 return binding_count;
794}
795
796S32 LLViewerKeyboard::loadBindingMode(const LLViewerKeyboard::KeyMode& keymode)
797{
798 S32 binding_count = 0;
799 for (LLInitParam::ParamIterator<KeyBinding>::const_iterator it = keymode.bindings.begin(),
800 end_it = keymode.bindings.end();
801 it != end_it;
802 ++it)
803 {
804 KEY key;
805 MASK mask;
806 LLKeyboard::keyFromString(it->key, &key);
807 LLKeyboard::maskFromString(it->mask, &mask);
808 bindKey(keymode.mode, key, mask, it->command);
809 binding_count++;
810 }
811
812 return binding_count;
813}
814
815S32 LLViewerKeyboard::loadBindings(const std::string& filename)
816{
817 LLFILE *fp;
818 const S32 BUFFER_SIZE = 2048;
819 char buffer[BUFFER_SIZE]; /* Flawfinder: ignore */
820 // *NOTE: This buffer size is hard coded into scanf() below.
821 char mode_string[MAX_STRING] = ""; /* Flawfinder: ignore */
822 char key_string[MAX_STRING] = ""; /* Flawfinder: ignore */
823 char mask_string[MAX_STRING] = ""; /* Flawfinder: ignore */
824 char function_string[MAX_STRING] = ""; /* Flawfinder: ignore */
825 S32 mode = MODE_THIRD_PERSON;
826 KEY key = 0;
827 MASK mask = 0;
828 S32 tokens_read;
829 S32 binding_count = 0;
830 S32 line_count = 0;
831
832 if(filename.empty())
833 {
834 llerrs << " No filename specified" << llendl;
835 return 0;
836 }
837
838 fp = LLFile::fopen(filename, "r");
839
840 if (!fp)
841 {
842 return 0;
843 }
844
845
846 while (!feof(fp))
847 {
848 line_count++;
849 if (!fgets(buffer, BUFFER_SIZE, fp))
850 break;
851
852 // skip over comments, blank lines
853 if (buffer[0] == '#' || buffer[0] == '\n') continue;
854
855 // grab the binding strings
856 tokens_read = sscanf( /* Flawfinder: ignore */
857 buffer,
858 "%254s %254s %254s %254s",
859 mode_string,
860 key_string,
861 mask_string,
862 function_string);
863
864 if (tokens_read == EOF)
865 {
866 llinfos << "Unexpected end-of-file at line " << line_count << " of key binding file " << filename << llendl;
867 fclose(fp);
868 return 0;
869 }
870 else if (tokens_read < 4)
871 {
872 llinfos << "Can't read line " << line_count << " of key binding file " << filename << llendl;
873 continue;
874 }
875
876 // convert mode
877 if (!modeFromString(mode_string, &mode))
878 {
879 llinfos << "Unknown mode on line " << line_count << " of key binding file " << filename << llendl;
880 llinfos << "Mode must be one of FIRST_PERSON, THIRD_PERSON, EDIT, EDIT_AVATAR" << llendl;
881 continue;
882 }
883
884 // convert key
885 if (!LLKeyboard::keyFromString(key_string, &key))
886 {
887 llinfos << "Can't interpret key on line " << line_count << " of key binding file " << filename << llendl;
888 continue;
889 }
890
891 // convert mask
892 if (!LLKeyboard::maskFromString(mask_string, &mask))
893 {
894 llinfos << "Can't interpret mask on line " << line_count << " of key binding file " << filename << llendl;
895 continue;
896 }
897
898 // bind key
899 if (bindKey(mode, key, mask, function_string))
900 {
901 binding_count++;
902 }
903 }
904
905 fclose(fp);
906
907 return binding_count;
908}
909
910
911EKeyboardMode LLViewerKeyboard::getMode()
912{
913 if ( gAgentCamera.cameraMouselook() )
914 {
915 return MODE_FIRST_PERSON;
916 }
917 else if ( gMorphView && gMorphView->getVisible())
918 {
919 return MODE_EDIT_AVATAR;
920 }
921 else if (isAgentAvatarValid() && gAgentAvatarp->isSitting())
922 {
923 return MODE_SITTING;
924 }
925 else
926 {
927 return MODE_THIRD_PERSON;
928 }
929}
930
931
932// Called from scanKeyboard.
933void LLViewerKeyboard::scanKey(KEY key, BOOL key_down, BOOL key_up, BOOL key_level)
934{
935 S32 mode = getMode();
936 // Consider keyboard scanning as NOT mouse event. JC
937 MASK mask = gKeyboard->currentMask(FALSE);
938
939 LLKeyBinding* binding = mBindings[mode];
940 S32 binding_count = mBindingCount[mode];
941
942
943 if (mKeyHandledByUI[key])
944 {
945 return;
946 }
947
948 // don't process key down on repeated keys
949 BOOL repeat = gKeyboard->getKeyRepeated(key);
950
951 for (S32 i = 0; i < binding_count; i++)
952 {
953 //for (S32 key = 0; key < KEY_COUNT; key++)
954 if (binding[i].mKey == key)
955 {
956 //if (binding[i].mKey == key && binding[i].mMask == mask)
957 if (binding[i].mMask == mask)
958 {
959 if (key_down && !repeat)
960 {
961 // ...key went down this frame, call function
962 binding[i].mFunction( KEYSTATE_DOWN );
963 }
964 else if (key_up)
965 {
966 // ...key went down this frame, call function
967 binding[i].mFunction( KEYSTATE_UP );
968 }
969 else if (key_level)
970 {
971 // ...key held down from previous frame
972 // Not windows, just call the function.
973 binding[i].mFunction( KEYSTATE_LEVEL );
974 }//if
975 }//if
976 }//for
977 }//for
978}