//----------------------------------------------------------------------------- // // Copyright 1993-1996 id Software // Copyright 1999-2016 Randy Heit // Copyright 2002-2016 Christoph Oelckers // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see http://www.gnu.org/licenses/ // // //----------------------------------------------------------------------------- #include "c_bind.h" #include "d_event.h" #include "d_gui.h" #include "inputstate.h" int eventhead; int eventtail; event_t events[NUM_EVENTS]; //========================================================================== // // G_Responder // Process the event for the game // //========================================================================== bool G_Responder (event_t *ev) { #if 0 // any other key pops up menu if in demos // [RH] But only if the key isn't bound to a "special" command if (gameaction == ga_nothing && (demoplayback || gamestate == GS_DEMOSCREEN || gamestate == GS_TITLELEVEL)) { const char *cmd = Bindings.GetBind (ev->data1); if (ev->type == EV_KeyDown) { if (!cmd || ( strnicmp (cmd, "menu_", 5) && stricmp (cmd, "toggleconsole") && stricmp (cmd, "sizeup") && stricmp (cmd, "sizedown") && stricmp (cmd, "togglemap") && stricmp (cmd, "spynext") && stricmp (cmd, "spyprev") && stricmp (cmd, "chase") && stricmp (cmd, "+showscores") && stricmp (cmd, "bumpgamma") && stricmp (cmd, "screenshot"))) { M_StartControlPanel(true); M_SetMenu(NAME_Mainmenu, -1); return true; } else { return C_DoKey (ev, &Bindings, &DoubleBindings); } } if (cmd && cmd[0] == '+') return C_DoKey (ev, &Bindings, &DoubleBindings); return false; } #endif #if 0 if (gamestate == GS_LEVEL) { if (ST_Responder (ev)) return true; // status window ate it if (!viewactive && primaryLevel->automap->Responder (ev, false)) return true; // automap ate it } else if (gamestate == GS_FINALE) { if (F_Responder (ev)) return true; // finale ate the event } #endif switch (ev->type) { case EV_KeyDown: if (C_DoKey (ev, &Bindings, &DoubleBindings)) return true; break; case EV_KeyUp: C_DoKey (ev, &Bindings, &DoubleBindings); break; #if 0 // [RH] mouse buttons are sent as key up/down events case EV_Mouse: mousex = (int)(ev->x * mouse_sensitivity); mousey = (int)(ev->y * mouse_sensitivity); break; #endif } // [RH] If the view is active, give the automap a chance at // the events *last* so that any bound keys get precedence. #if 0 if (gamestate == GS_LEVEL && viewactive && primaryLevel->automap) return primaryLevel->automap->Responder (ev, true); #endif return (ev->type == EV_KeyDown || ev->type == EV_Mouse); } //========================================================================== // // D_ProcessEvents // // Send all the events of the given timestamp down the responder chain. // Events are asynchronous inputs generally generated by the game user. // Events can be discarded if no responder claims them // //========================================================================== void D_ProcessEvents (void) { event_t *ev; for (; eventtail != eventhead ; eventtail = (eventtail+1)&(NUM_EVENTS-1)) { ev = &events[eventtail]; if (ev->type == EV_None) continue; if (ev->type == EV_DeviceChange) (void)0;//UpdateJoystickMenu(I_UpdateDeviceList()); #if 0 if (C_Responder (ev)) continue; // console ate the event if (M_Responder (ev)) continue; // menu ate the event #endif G_Responder (ev); } } //========================================================================== // // D_PostEvent // // Called by the I/O functions when input is detected. // //========================================================================== void D_PostEvent (const event_t *ev) { // Do not post duplicate consecutive EV_DeviceChange events. if (ev->type == EV_DeviceChange && events[eventhead].type == EV_DeviceChange) { return; } events[eventhead] = *ev; #if 0 // No idea if this can be made to work ever... For now, pass on the mouse movement event so that the input code can deal with them itself. if (ev->type == EV_Mouse && menuactive == MENU_Off && ConsoleState != c_down && ConsoleState != c_falling && !primaryLevel->localEventManager->Responder(ev) && !paused) { if (Button_Mlook.bDown || freelook) { int look = int(ev->y * m_pitch * mouse_sensitivity * 16.0); if (invertmouse) look = -look; G_AddViewPitch (look, true); events[eventhead].y = 0; } if (!Button_Strafe.bDown && !lookstrafe) { G_AddViewAngle (int(ev->x * m_yaw * mouse_sensitivity * 8.0), true); events[eventhead].x = 0; } if ((events[eventhead].x | events[eventhead].y) == 0) { return; } } #endif // Add the key to the global keyboard state. // This is probably the biggest roadblock with the input system as it undermines a proper event driven approach. // Too much code depends on just checking this instead of waiting for events to happen. // Here's also definitely not the best place to maintain the keyboard state but right now it's unavoidable to do this outside the event processing because so much code depends on it. // Once all those busy waiting loops can poll the event queue for a proper skip event, this will mostly go away. inputState.AddEvent(ev); // Also add it to the event queue - this is where everything should transition to eventually. eventhead = (eventhead+1)&(NUM_EVENTS-1); } //========================================================================== // // D_RemoveNextCharEvent // // Removes the next EV_GUI_Char event in the input queue. Used by the menu, // since it (generally) consumes EV_GUI_KeyDown events and not EV_GUI_Char // events, and it needs to ensure that there is no left over input when it's // done. If there are multiple EV_GUI_KeyDowns before the EV_GUI_Char, then // there are dead chars involved, so those should be removed, too. We do // this by changing the message type to EV_None rather than by actually // removing the event from the queue. // //========================================================================== void D_RemoveNextCharEvent() { assert(events[eventtail].type == EV_GUI_Event && events[eventtail].subtype == EV_GUI_KeyDown); for (int evnum = eventtail; evnum != eventhead; evnum = (evnum+1) & (NUM_EVENTS-1)) { event_t *ev = &events[evnum]; if (ev->type != EV_GUI_Event) break; if (ev->subtype == EV_GUI_KeyDown || ev->subtype == EV_GUI_Char) { ev->type = EV_None; if (ev->subtype == EV_GUI_Char) break; } else { break; } } }