<commit_before>/* bzflag
 * Copyright (c) 1993 - 2004 Tim Riker
 *
 * This package is free software;  you can redistribute it and/or
 * modify it under the terms of the license found in the file
 * named LICENSE that should have accompanied this file.
 *
 * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */

/* bzflag special common - 1st one */
#include "common.h"

#include <iostream>
#include <string>

#include "ActionBinding.h"
#include "CommandManager.h"
#include "KeyManager.h"

// initialize the singleton
template <>
ActionBinding* Singleton<ActionBinding>::_instance = (ActionBinding*)0;

ActionBinding::ActionBinding() {
  wayToBindActions.insert(std::make_pair(std::string("quit"), press));
  wayToBindActions.insert(std::make_pair(std::string("fire"), both));
  wayToBindActions.insert(std::make_pair(std::string("drop"), press));
  wayToBindActions.insert(std::make_pair(std::string("identify"), press));
  wayToBindActions.insert(std::make_pair(std::string("jump"), press));
  wayToBindActions.insert(std::make_pair(std::string("send all"), press));
  wayToBindActions.insert(std::make_pair(std::string("send team"), press));
  wayToBindActions.insert(std::make_pair(std::string("send nemesis"), press));
  wayToBindActions.insert(std::make_pair(std::string("send recipient"), press));
  wayToBindActions.insert(std::make_pair(std::string("send admin"), press));
  wayToBindActions.insert(std::make_pair(std::string("toggle displayScore"), press));
  wayToBindActions.insert(std::make_pair(std::string("toggle displayBinoculars"), press));
  wayToBindActions.insert(std::make_pair(std::string("pause"), press));
  wayToBindActions.insert(std::make_pair(std::string("fullscreen"), press));
  wayToBindActions.insert(std::make_pair(std::string("iconify"), press));
#ifdef SNAPPING
  wayToBindActions.insert(std::make_pair(std::string("screenshot"), press));
#endif
  wayToBindActions.insert(std::make_pair(std::string("time backward"), press));
  wayToBindActions.insert(std::make_pair(std::string("time forward"), press));
  wayToBindActions.insert(std::make_pair(std::string("toggleConsoleAndRadar"), press));
  wayToBindActions.insert(std::make_pair(std::string("toggleFlags radar"), press));
  wayToBindActions.insert(std::make_pair(std::string("toggleFlags main"), press));
  wayToBindActions.insert(std::make_pair(std::string("silence"), press));
  wayToBindActions.insert(std::make_pair(std::string("toggle displayLabels"), press));
  wayToBindActions.insert(std::make_pair(std::string("destruct"), press));

  // Movement keys
  wayToBindActions.insert(std::make_pair(std::string("turn left"), both));
  wayToBindActions.insert(std::make_pair(std::string("turn right"), both));
  wayToBindActions.insert(std::make_pair(std::string("drive forward"), both));
  wayToBindActions.insert(std::make_pair(std::string("drive reverse"), both));
  // End movement keys

  wayToBindActions.insert(std::make_pair(std::string("roam cycle subject backward"),
					 press));
  wayToBindActions.insert(std::make_pair(std::string("roam cycle subject forward"), press));
  wayToBindActions.insert(std::make_pair(std::string("roam cycle type forward"), press));
  wayToBindActions.insert(std::make_pair(std::string("roam zoom in"), both));
  wayToBindActions.insert(std::make_pair(std::string("roam zoom out"), both));
  wayToBindActions.insert(std::make_pair(std::string("roam zoom normal"), both));
  wayToBindActions.insert(std::make_pair(std::string("servercommand"), press));
  wayToBindActions.insert(std::make_pair(std::string("toggle displayFlagHelp"), press));
  wayToBindActions.insert(std::make_pair(std::string("scrollpanel up"), press));
  wayToBindActions.insert(std::make_pair(std::string("scrollpanel down"), press));
  wayToBindActions.insert(std::make_pair(std::string("radarZoom in"), press));
  wayToBindActions.insert(std::make_pair(std::string("radarZoom out"), press));
  wayToBindActions.insert(std::make_pair(std::string("set displayRadarRange 0.25"), press));
  wayToBindActions.insert(std::make_pair(std::string("set displayRadarRange 0.5"), press));
  wayToBindActions.insert(std::make_pair(std::string("set displayRadarRange 1.0"), press));
  wayToBindActions.insert(std::make_pair(std::string("toggle slowKeyboard"), press));
  wayToBindActions.insert(std::make_pair(std::string("hunt"), press));
  wayToBindActions.insert(std::make_pair(std::string("restart"), release));
  wayToBindActions.insert(std::make_pair(std::string("autopilot"), press));

  wayToBindActions.insert(std::make_pair(std::string("messagepanel all"),
					 press));
  wayToBindActions.insert(std::make_pair(std::string("messagepanel chat"),
					 press));
  wayToBindActions.insert(std::make_pair(std::string("messagepanel server"),
					 press));
  wayToBindActions.insert(std::make_pair(std::string("messagepanel misc"),
					 press));

  defaultBinding.insert(std::make_pair(std::string("F12"), std::string("quit")));
  defaultBinding.insert(std::make_pair(std::string("Left Mouse"), std::string("fire")));
  defaultBinding.insert(std::make_pair(std::string("Enter"), std::string("fire")));
  defaultBinding.insert(std::make_pair(std::string("Middle Mouse"), std::string("drop")));
  defaultBinding.insert(std::make_pair(std::string("Space"), std::string("drop")));
  defaultBinding.insert(std::make_pair(std::string("Right Mouse"), std::string("identify")));
  defaultBinding.insert(std::make_pair(std::string("I"), std::string("identify")));
  defaultBinding.insert(std::make_pair(std::string("Tab"), std::string("jump")));
  defaultBinding.insert(std::make_pair(std::string("N"), std::string("send all")));
  defaultBinding.insert(std::make_pair(std::string("M"), std::string("send team")));
  defaultBinding.insert(std::make_pair(std::string(","), std::string("send nemesis")));
  defaultBinding.insert(std::make_pair(std::string("."), std::string("send recipient")));
  defaultBinding.insert(std::make_pair(std::string("Z"), std::string("send admin")));
  defaultBinding.insert(std::make_pair(std::string("S"), std::string("toggle displayScore")));
  defaultBinding.insert(std::make_pair(std::string("B"), std::string("toggle displayBinoculars")));
  defaultBinding.insert(std::make_pair(std::string("Pause"), std::string("pause")));
  defaultBinding.insert(std::make_pair(std::string("P"), std::string("pause")));
  defaultBinding.insert(std::make_pair(std::string("F1"), std::string("fullscreen")));
  defaultBinding.insert(std::make_pair(std::string("F3"), std::string("toggleConsoleAndRadar")));
  defaultBinding.insert(std::make_pair(std::string("F4"), std::string("iconify")));
#ifdef SNAPPING
  defaultBinding.insert(std::make_pair(std::string("F5"), std::string("screenshot")));
#endif
  defaultBinding.insert(std::make_pair(std::string("-"), std::string("time backward")));
  defaultBinding.insert(std::make_pair(std::string("="), std::string("time forward")));
  defaultBinding.insert(std::make_pair(std::string("H"), std::string("toggleFlags radar")));
  defaultBinding.insert(std::make_pair(std::string("J"), std::string("toggleFlags main")));
  defaultBinding.insert(std::make_pair(std::string("K"), std::string("silence")));
  defaultBinding.insert(std::make_pair(std::string("L"), std::string("toggle displayLabels")));
  defaultBinding.insert(std::make_pair(std::string("Delete"), std::string("destruct")));

  // Default movement keys
  defaultBinding.insert(std::make_pair(std::string("Left Arrow"),
				       std::string("turn left")));
  defaultBinding.insert(std::make_pair(std::string("Right Arrow"),
				       std::string("turn right")));
  defaultBinding.insert(std::make_pair(std::string("Up Arrow"),
				       std::string("drive forward")));
  defaultBinding.insert(std::make_pair(std::string("Down Arrow"),
				       std::string("drive reverse")));
  // End default movement keys

  defaultBinding.insert(std::make_pair(std::string("Wheel Up"),
				       std::string("radarZoom in")));
  defaultBinding.insert(std::make_pair(std::string("Wheel Down"),
				       std::string("radarZoom out")));

  defaultBinding.insert(std::make_pair(std::string("F6"), std::string("roam cycle subject backward")));
  defaultBinding.insert(std::make_pair(std::string("F7"), std::string("roam cycle subject forward")));
  defaultBinding.insert(std::make_pair(std::string("F8"), std::string("roam cycle type forward")));
  defaultBinding.insert(std::make_pair(std::string("F9"), std::string("roam zoom in")));
  defaultBinding.insert(std::make_pair(std::string("F10"), std::string("roam zoom out")));
  defaultBinding.insert(std::make_pair(std::string("F11"), std::string("roam zoom normal")));
  defaultBinding.insert(std::make_pair(std::string("O"), std::string("servercommand")));
  defaultBinding.insert(std::make_pair(std::string("F"), std::string("toggle displayFlagHelp")));
  defaultBinding.insert(std::make_pair(std::string("Page Up"), std::string("scrollpanel up")));
  defaultBinding.insert(std::make_pair(std::string("Page Down"), std::string("scrollpanel down")));
  defaultBinding.insert(std::make_pair(std::string("1"), std::string("set displayRadarRange 0.25")));
  defaultBinding.insert(std::make_pair(std::string("2"), std::string("set displayRadarRange 0.5")));
  defaultBinding.insert(std::make_pair(std::string("3"), std::string("set displayRadarRange 1.0")));
  defaultBinding.insert(std::make_pair(std::string("A"), std::string("toggle slowKeyboard")));
  defaultBinding.insert(std::make_pair(std::string("U"), std::string("hunt")));
  defaultBinding.insert(std::make_pair(std::string("Right Mouse"), std::string("restart")));
  defaultBinding.insert(std::make_pair(std::string("I"), std::string("restart")));
  defaultBinding.insert(std::make_pair(std::string("9"), std::string("autopilot")));

  defaultBinding.insert(std::make_pair(std::string("Shift+F1"),
				       std::string("messagepanel all")));
  defaultBinding.insert(std::make_pair(std::string("Shift+F2"),
				       std::string("messagepanel chat")));
  defaultBinding.insert(std::make_pair(std::string("Shift+F3"),
				       std::string("messagepanel server")));
  defaultBinding.insert(std::make_pair(std::string("Shift+F4"),
				       std::string("messagepanel misc")));
}

void ActionBinding::resetBindings() {
  BindingTable::const_iterator index;

  for (index = bindingTable.begin();
       index != bindingTable.end();
       ++index)
    unbind(index->second, index->first);

  bindingTable = defaultBinding;

  for (index = bindingTable.begin();
       index != bindingTable.end();
       ++index)
    bind(index->second, index->first);
}

void ActionBinding::getFromBindings() {
  bindingTable.clear();
  KEYMGR.iterate(&onScanCB, this);
}

void ActionBinding::onScanCB(const std::string& name, bool,
			     const std::string& cmd, void*)
{
  ActionBinding::instance().associate(name, cmd, false);
}

void ActionBinding::associate(std::string key,
			      std::string action,
			      bool        keyBind) {
  BindingTable::iterator index, next;
  if (!wayToBindActions.count(action))
    return;
  PressStatusBind newStatusBind = wayToBindActions[action];
  for (index = bindingTable.lower_bound( key ); index != bindingTable.upper_bound( key ); index = next) {
    next = index;
    ++next;
    if (newStatusBind == both) {
      if (keyBind)
	unbind(index->second, key);
      bindingTable.erase(index);
    } else if (newStatusBind == press) {
      if (wayToBindActions[index->second] != release) {
	if (keyBind)
	  unbind(index->second, key);
	bindingTable.erase(index);
      }
    } else {
      if (wayToBindActions[index->second] != press) {
	if (keyBind)
	  unbind(index->second, key);
	bindingTable.erase(index);
      }
    }
  }
  bindingTable.insert(std::make_pair(key, action));
  if (keyBind)
    bind(action, key);
};

void ActionBinding::deassociate(std::string action) {
  BindingTable::iterator index, next;
  for (index = bindingTable.begin();
       index != bindingTable.end();
       index = next) {
    next = index;
    ++next;
    if (index->second == action) {
      unbind(action, index->first);
      bindingTable.erase(index);
    }
  }
};

void ActionBinding::bind(std::string action, std::string key) {
  PressStatusBind statusBind = wayToBindActions[action];
  std::string command;
  if (statusBind == press || statusBind == both) {
    command = "bind \"" + key + "\" down \"" + action + "\"";
    CMDMGR.run(command);
  };
  if (statusBind == release || statusBind == both) {
    command = "bind \"" + key + "\" up \"" + action + "\"";
    CMDMGR.run(command);
  };
}

void ActionBinding::unbind(std::string action, std::string key) {
  PressStatusBind statusBind = wayToBindActions[action];
  std::string command;
  if (statusBind == press || statusBind == both) {
    command = "unbind \"" + key + "\" down";
    CMDMGR.run(command);
  };
  if (statusBind == release || statusBind == both) {
    command = "unbind \"" + key + "\" up";
    CMDMGR.run(command);
  };
}

// Local Variables: ***
// mode:C++ ***
// tab-width: 8 ***
// c-basic-offset: 2 ***
// indent-tabs-mode: t ***
// End: ***
// ex: shiftwidth=2 tabstop=8
<commit_msg>Using wheel to scroll chat area, moving RadarZoom to Wheel+Shift<commit_after>/* bzflag
 * Copyright (c) 1993 - 2004 Tim Riker
 *
 * This package is free software;  you can redistribute it and/or
 * modify it under the terms of the license found in the file
 * named LICENSE that should have accompanied this file.
 *
 * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */

/* bzflag special common - 1st one */
#include "common.h"

#include <iostream>
#include <string>

#include "ActionBinding.h"
#include "CommandManager.h"
#include "KeyManager.h"

// initialize the singleton
template <>
ActionBinding* Singleton<ActionBinding>::_instance = (ActionBinding*)0;

ActionBinding::ActionBinding() {
  wayToBindActions.insert(std::make_pair(std::string("quit"), press));
  wayToBindActions.insert(std::make_pair(std::string("fire"), both));
  wayToBindActions.insert(std::make_pair(std::string("drop"), press));
  wayToBindActions.insert(std::make_pair(std::string("identify"), press));
  wayToBindActions.insert(std::make_pair(std::string("jump"), press));
  wayToBindActions.insert(std::make_pair(std::string("send all"), press));
  wayToBindActions.insert(std::make_pair(std::string("send team"), press));
  wayToBindActions.insert(std::make_pair(std::string("send nemesis"), press));
  wayToBindActions.insert(std::make_pair(std::string("send recipient"), press));
  wayToBindActions.insert(std::make_pair(std::string("send admin"), press));
  wayToBindActions.insert(std::make_pair(std::string("toggle displayScore"), press));
  wayToBindActions.insert(std::make_pair(std::string("toggle displayBinoculars"), press));
  wayToBindActions.insert(std::make_pair(std::string("pause"), press));
  wayToBindActions.insert(std::make_pair(std::string("fullscreen"), press));
  wayToBindActions.insert(std::make_pair(std::string("iconify"), press));
#ifdef SNAPPING
  wayToBindActions.insert(std::make_pair(std::string("screenshot"), press));
#endif
  wayToBindActions.insert(std::make_pair(std::string("time backward"), press));
  wayToBindActions.insert(std::make_pair(std::string("time forward"), press));
  wayToBindActions.insert(std::make_pair(std::string("toggleConsoleAndRadar"), press));
  wayToBindActions.insert(std::make_pair(std::string("toggleFlags radar"), press));
  wayToBindActions.insert(std::make_pair(std::string("toggleFlags main"), press));
  wayToBindActions.insert(std::make_pair(std::string("silence"), press));
  wayToBindActions.insert(std::make_pair(std::string("toggle displayLabels"), press));
  wayToBindActions.insert(std::make_pair(std::string("destruct"), press));

  // Movement keys
  wayToBindActions.insert(std::make_pair(std::string("turn left"), both));
  wayToBindActions.insert(std::make_pair(std::string("turn right"), both));
  wayToBindActions.insert(std::make_pair(std::string("drive forward"), both));
  wayToBindActions.insert(std::make_pair(std::string("drive reverse"), both));
  // End movement keys

  wayToBindActions.insert(std::make_pair(std::string("roam cycle subject backward"),
					 press));
  wayToBindActions.insert(std::make_pair(std::string("roam cycle subject forward"), press));
  wayToBindActions.insert(std::make_pair(std::string("roam cycle type forward"), press));
  wayToBindActions.insert(std::make_pair(std::string("roam zoom in"), both));
  wayToBindActions.insert(std::make_pair(std::string("roam zoom out"), both));
  wayToBindActions.insert(std::make_pair(std::string("roam zoom normal"), both));
  wayToBindActions.insert(std::make_pair(std::string("servercommand"), press));
  wayToBindActions.insert(std::make_pair(std::string("toggle displayFlagHelp"), press));
  wayToBindActions.insert(std::make_pair(std::string("scrollpanel up"), press));
  wayToBindActions.insert(std::make_pair(std::string("scrollpanel down"), press));
  wayToBindActions.insert(std::make_pair(std::string("radarZoom in"), press));
  wayToBindActions.insert(std::make_pair(std::string("radarZoom out"), press));
  wayToBindActions.insert(std::make_pair(std::string("set displayRadarRange 0.25"), press));
  wayToBindActions.insert(std::make_pair(std::string("set displayRadarRange 0.5"), press));
  wayToBindActions.insert(std::make_pair(std::string("set displayRadarRange 1.0"), press));
  wayToBindActions.insert(std::make_pair(std::string("toggle slowKeyboard"), press));
  wayToBindActions.insert(std::make_pair(std::string("hunt"), press));
  wayToBindActions.insert(std::make_pair(std::string("restart"), release));
  wayToBindActions.insert(std::make_pair(std::string("autopilot"), press));

  wayToBindActions.insert(std::make_pair(std::string("messagepanel all"),
					 press));
  wayToBindActions.insert(std::make_pair(std::string("messagepanel chat"),
					 press));
  wayToBindActions.insert(std::make_pair(std::string("messagepanel server"),
					 press));
  wayToBindActions.insert(std::make_pair(std::string("messagepanel misc"),
					 press));

  defaultBinding.insert(std::make_pair(std::string("F12"), std::string("quit")));
  defaultBinding.insert(std::make_pair(std::string("Left Mouse"), std::string("fire")));
  defaultBinding.insert(std::make_pair(std::string("Enter"), std::string("fire")));
  defaultBinding.insert(std::make_pair(std::string("Middle Mouse"), std::string("drop")));
  defaultBinding.insert(std::make_pair(std::string("Space"), std::string("drop")));
  defaultBinding.insert(std::make_pair(std::string("Right Mouse"), std::string("identify")));
  defaultBinding.insert(std::make_pair(std::string("I"), std::string("identify")));
  defaultBinding.insert(std::make_pair(std::string("Tab"), std::string("jump")));
  defaultBinding.insert(std::make_pair(std::string("N"), std::string("send all")));
  defaultBinding.insert(std::make_pair(std::string("M"), std::string("send team")));
  defaultBinding.insert(std::make_pair(std::string(","), std::string("send nemesis")));
  defaultBinding.insert(std::make_pair(std::string("."), std::string("send recipient")));
  defaultBinding.insert(std::make_pair(std::string("Z"), std::string("send admin")));
  defaultBinding.insert(std::make_pair(std::string("S"), std::string("toggle displayScore")));
  defaultBinding.insert(std::make_pair(std::string("B"), std::string("toggle displayBinoculars")));
  defaultBinding.insert(std::make_pair(std::string("Pause"), std::string("pause")));
  defaultBinding.insert(std::make_pair(std::string("P"), std::string("pause")));
  defaultBinding.insert(std::make_pair(std::string("F1"), std::string("fullscreen")));
  defaultBinding.insert(std::make_pair(std::string("F3"), std::string("toggleConsoleAndRadar")));
  defaultBinding.insert(std::make_pair(std::string("F4"), std::string("iconify")));
#ifdef SNAPPING
  defaultBinding.insert(std::make_pair(std::string("F5"), std::string("screenshot")));
#endif
  defaultBinding.insert(std::make_pair(std::string("-"), std::string("time backward")));
  defaultBinding.insert(std::make_pair(std::string("="), std::string("time forward")));
  defaultBinding.insert(std::make_pair(std::string("H"), std::string("toggleFlags radar")));
  defaultBinding.insert(std::make_pair(std::string("J"), std::string("toggleFlags main")));
  defaultBinding.insert(std::make_pair(std::string("K"), std::string("silence")));
  defaultBinding.insert(std::make_pair(std::string("L"), std::string("toggle displayLabels")));
  defaultBinding.insert(std::make_pair(std::string("Delete"), std::string("destruct")));

  // Default movement keys
  defaultBinding.insert(std::make_pair(std::string("Left Arrow"),
				       std::string("turn left")));
  defaultBinding.insert(std::make_pair(std::string("Right Arrow"),
				       std::string("turn right")));
  defaultBinding.insert(std::make_pair(std::string("Up Arrow"),
				       std::string("drive forward")));
  defaultBinding.insert(std::make_pair(std::string("Down Arrow"),
				       std::string("drive reverse")));
  // End default movement keys

  defaultBinding.insert(std::make_pair(std::string("Shift+Wheel Up"),
				       std::string("radarZoom in")));
  defaultBinding.insert(std::make_pair(std::string("Shift+Wheel Down"),
				       std::string("radarZoom out")));

  defaultBinding.insert(std::make_pair(std::string("F6"), std::string("roam cycle subject backward")));
  defaultBinding.insert(std::make_pair(std::string("F7"), std::string("roam cycle subject forward")));
  defaultBinding.insert(std::make_pair(std::string("F8"), std::string("roam cycle type forward")));
  defaultBinding.insert(std::make_pair(std::string("F9"), std::string("roam zoom in")));
  defaultBinding.insert(std::make_pair(std::string("F10"), std::string("roam zoom out")));
  defaultBinding.insert(std::make_pair(std::string("F11"), std::string("roam zoom normal")));
  defaultBinding.insert(std::make_pair(std::string("O"), std::string("servercommand")));
  defaultBinding.insert(std::make_pair(std::string("F"), std::string("toggle displayFlagHelp")));
  defaultBinding.insert(std::make_pair(std::string("Page Up"),
				       std::string("scrollpanel up")));
  defaultBinding.insert(std::make_pair(std::string("Wheel Up"),
				       std::string("scrollpanel up")));
  defaultBinding.insert(std::make_pair(std::string("Wheel Down"),
				       std::string("scrollpanel down")));
  defaultBinding.insert(std::make_pair(std::string("1"), std::string("set displayRadarRange 0.25")));
  defaultBinding.insert(std::make_pair(std::string("2"), std::string("set displayRadarRange 0.5")));
  defaultBinding.insert(std::make_pair(std::string("3"), std::string("set displayRadarRange 1.0")));
  defaultBinding.insert(std::make_pair(std::string("A"), std::string("toggle slowKeyboard")));
  defaultBinding.insert(std::make_pair(std::string("U"), std::string("hunt")));
  defaultBinding.insert(std::make_pair(std::string("Right Mouse"), std::string("restart")));
  defaultBinding.insert(std::make_pair(std::string("I"), std::string("restart")));
  defaultBinding.insert(std::make_pair(std::string("9"), std::string("autopilot")));

  defaultBinding.insert(std::make_pair(std::string("Shift+F1"),
				       std::string("messagepanel all")));
  defaultBinding.insert(std::make_pair(std::string("Shift+F2"),
				       std::string("messagepanel chat")));
  defaultBinding.insert(std::make_pair(std::string("Shift+F3"),
				       std::string("messagepanel server")));
  defaultBinding.insert(std::make_pair(std::string("Shift+F4"),
				       std::string("messagepanel misc")));
}

void ActionBinding::resetBindings() {
  BindingTable::const_iterator index;

  for (index = bindingTable.begin();
       index != bindingTable.end();
       ++index)
    unbind(index->second, index->first);

  bindingTable = defaultBinding;

  for (index = bindingTable.begin();
       index != bindingTable.end();
       ++index)
    bind(index->second, index->first);
}

void ActionBinding::getFromBindings() {
  bindingTable.clear();
  KEYMGR.iterate(&onScanCB, this);
}

void ActionBinding::onScanCB(const std::string& name, bool,
			     const std::string& cmd, void*)
{
  ActionBinding::instance().associate(name, cmd, false);
}

void ActionBinding::associate(std::string key,
			      std::string action,
			      bool        keyBind) {
  BindingTable::iterator index, next;
  if (!wayToBindActions.count(action))
    return;
  PressStatusBind newStatusBind = wayToBindActions[action];
  for (index = bindingTable.lower_bound( key ); index != bindingTable.upper_bound( key ); index = next) {
    next = index;
    ++next;
    if (newStatusBind == both) {
      if (keyBind)
	unbind(index->second, key);
      bindingTable.erase(index);
    } else if (newStatusBind == press) {
      if (wayToBindActions[index->second] != release) {
	if (keyBind)
	  unbind(index->second, key);
	bindingTable.erase(index);
      }
    } else {
      if (wayToBindActions[index->second] != press) {
	if (keyBind)
	  unbind(index->second, key);
	bindingTable.erase(index);
      }
    }
  }
  bindingTable.insert(std::make_pair(key, action));
  if (keyBind)
    bind(action, key);
};

void ActionBinding::deassociate(std::string action) {
  BindingTable::iterator index, next;
  for (index = bindingTable.begin();
       index != bindingTable.end();
       index = next) {
    next = index;
    ++next;
    if (index->second == action) {
      unbind(action, index->first);
      bindingTable.erase(index);
    }
  }
};

void ActionBinding::bind(std::string action, std::string key) {
  PressStatusBind statusBind = wayToBindActions[action];
  std::string command;
  if (statusBind == press || statusBind == both) {
    command = "bind \"" + key + "\" down \"" + action + "\"";
    CMDMGR.run(command);
  };
  if (statusBind == release || statusBind == both) {
    command = "bind \"" + key + "\" up \"" + action + "\"";
    CMDMGR.run(command);
  };
}

void ActionBinding::unbind(std::string action, std::string key) {
  PressStatusBind statusBind = wayToBindActions[action];
  std::string command;
  if (statusBind == press || statusBind == both) {
    command = "unbind \"" + key + "\" down";
    CMDMGR.run(command);
  };
  if (statusBind == release || statusBind == both) {
    command = "unbind \"" + key + "\" up";
    CMDMGR.run(command);
  };
}

// Local Variables: ***
// mode:C++ ***
// tab-width: 8 ***
// c-basic-offset: 2 ***
// indent-tabs-mode: t ***
// End: ***
// ex: shiftwidth=2 tabstop=8
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2014 David Wicks, sansumbrella.com
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or
 * without modification, are permitted provided that the following
 * conditions are met:
 *
 * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

 #pragma once

namespace choreograph
{

// A motion describes a one-dimensional change through time.
// These can be ease equations, always return the same value, or sample some data. Whatever you want.
// For classic motion, EaseFn( 0 ) = 0, EaseFn( 1 ) = 1.
// set( 0.0f ).move( )
// then again, might just make a curve struct that is callable and have a way to set its initial and final derivatives.
typedef std::function<float (float)> EaseFn;

struct Hold
{
  float operator() ( float t ) const { return 0.0f; }
};

struct LinearRamp
{
  float operator() ( float t ) const { return t; }
};

inline float easeNone( float t ) { return t; }

//! A Position describes a point in time.
template<typename T>
struct Position
{
  Position() = default;

  Position( const T &value, float time = 0.0f ):
    time( time ),
    value( value )
  {}

  float time = 0.0f;
  T     value;
};

//! The default templated lerp function.
template<typename T>
T lerpT( const T &a, const T &b, float t )
{
  return a + (b - a) * t;
}

/**
 A Phrase is a part of a Sequence.
 It describes the motion between two positions.
 This is the essence of a Tween, with all values held internally.
 */
template<typename T>
class Phrase
{
public:
  using LerpFn = std::function<T (const T&, const T&, float)>;

  Phrase( const T &end, float duration, const EaseFn &easeFn = &easeNone ):
    end( end, duration ),
    motion( easeFn )
  {}

  Phrase( const Position<T> &start, const Position<T> &end, const EaseFn &easeFn = &easeNone ):
    start( start ),
    end( end ),
    motion( easeFn )
  {}

  virtual ~Phrase() = default;

  //! Set start time to \a time. End time is adjusted to preserve duration.
  void shiftStartTimeTo( float time ) {
    float delta = time - start.time;
    start.time = time;
    end.time += delta;
  }

  //! Change start value to \a value.
  void setStartValue( const T &value )
  {
    start.value = value;
  }

  //! Returns the value at the beginning of this phrase.
  inline const T& getStartValue() const { return start.value; }
  //! Returns the value at the end of this phrase.
  inline const T& getEndValue() const { return end.value; }

  //! Returns the time at the beginning of this phrase.
  inline float getStartTime() const { return start.time; }
  //! Returns the time at the end of this phrase.
  inline float getEndTime() const { return end.time; }

  //! Returns normalized time if t is in range [start.time, end.time].
  inline float normalizeTime( float t ) const { return (t - start.time) / (end.time - start.time); }
  //! Returns the duration of this phrase.
  inline float getDuration() const { return end.time - start.time; }

  //! Returns the interpolated value at the given time.
  virtual T getValue( float atTime ) const
  {
    return lerpFn( start.value, end.value, motion( normalizeTime( atTime ) ) );
  }
private:
  Position<T> start;
  Position<T> end;
  EaseFn      motion;
  LerpFn      lerpFn = &lerpT<T>;
};

template<typename T>
using PhraseRef = std::shared_ptr<Phrase<T>>;

/**
 Phrase with separately-interpolated components.
 Allows for the use of separate ease functions per component.
 All components must be of the same type.
 */
template<typename T>
class Phrase2 : public Phrase<T>
{
public:
  Phrase2( const T &end, float duration, const EaseFn &ease_x, const EaseFn &ease_y ):
    Phrase<T>( end, duration ),
    motion_x( ease_x ),
    motion_y( ease_y )
  {}

  Phrase2( const Position<T> &start, const Position<T> &end, const EaseFn &ease_x, const EaseFn &ease_y ):
    Phrase<T>( start, end ),
    motion_x( ease_x ),
    motion_y( ease_y )
  {}

  //! Returns the interpolated value at the given time.
  T getValue( float atTime ) const override
  {
    float t = Phrase<T>::normalizeTime( atTime );
    return T( componentLerpFn( Phrase<T>::getStartValue().x, Phrase<T>::getEndValue().x, motion_x( t ) ),
             componentLerpFn( Phrase<T>::getStartValue().y, Phrase<T>::getEndValue().y, motion_y( t ) ) );
  }

  //! Set ease functions for first and second components.
  void setEase( const EaseFn &component_0, const EaseFn &component_1 ) {
    motion_x = component_0;
    motion_y = component_1;
  }

private:
  using ComponentT = decltype( T().x ); // get the type of the x component;
  using ComponentLerpFn = std::function<ComponentT (const ComponentT&, const ComponentT&, float)>;

  ComponentLerpFn componentLerpFn = &lerpT<ComponentT>;
  EaseFn          motion_x;
  EaseFn          motion_y;
};

using Phrase2v = Phrase2<ci::vec2>;

/**
 A Sequence of motions.
 Our essential compositional tool, describing all the transformations to one element.
 A kind of platonic idea of an animation sequence; this describes a motion without giving it an output.
*/
template<typename T>
class Sequence
{
public:
  // Sequences always need to have some valid value.
  Sequence() = delete;

  //! Construct a Sequence with an initial \a value.
  explicit Sequence( const T &value ):
    _initial_value( value )
  {}

  T getValue( float atTime );

  //! Set current value. An instantaneous hold.
  Sequence<T>& set( const T &value )
  {
    if( _segments.empty() ) {
      _initial_value = value;
    }
    else {
      hold( value, 0.0f );
    }
    return *this;
  }

  //! Returns a copy of this sequence. Useful if you want to make a base animation and modify that.
  std::shared_ptr<Sequence<T>> copy() const { return std::make_shared<Sequence<T>>( *this ); }

  //! Hold on current end value for \a duration seconds.
  Sequence<T>& wait( float duration ) { return hold( duration ); }

  //! Hold on current end value for \a duration seconds.
  Sequence<T>& hold( float duration )
  {
    return hold( endValue(), duration );
  }

  //! Hold on \a value for \a duration seconds.
  Sequence<T>& hold( const T &value, float duration )
  {
    Position<T> start{ value, _duration };
    Position<T> end{ value, _duration + duration };
    auto phrase = std::make_shared<Phrase<T>>( start, end, Hold() );

    _segments.push_back( phrase );
    _duration = phrase->getEndTime();

    return *this;
  }

  //! Animate to \a value over \a duration seconds using \a ease easing.
  Sequence<T>& rampTo( const T &value, float duration, const EaseFn &ease = LinearRamp() )
  {
    Position<T> start{ endValue(), _duration };
    Position<T> end{ value, _duration + duration };
    auto phrase = std::make_shared<Phrase<T>>( start, end, ease );

    _segments.push_back( phrase );

    _duration = phrase->getEndTime();

    return *this;
  }

  template<typename PhraseT, typename... Args>
  Sequence<T>& then( const T& value, float duration, Args... args )
  {
    Position<T> start{ endValue(), _duration };
    Position<T> end{ value, _duration + duration };
    auto phrase = std::make_shared<PhraseT>( start, end, args... );
    // Would expect the following to make my dream user syntax work.
//    auto phrase = std::make_shared<PhraseT<T>>( start, end, args... );

    _segments.push_back( phrase );
    _duration = phrase->getEndTime();

    return *this;
  }

  Sequence<T>& then( const PhraseRef<T>& phrase )
  {
    phrase->setStartValue( endValue() );
    phrase->shiftStartTimeTo( _duration );
    _duration = phrase->getEndTime();

    _segments.push_back( phrase );

    return *this;
  }

  //! Sets the ease function of the last Phrase in the Sequence.
  Sequence<T>& ease( const EaseFn &easeFn ) {
    if( ! _segments.empty() ) {
      _segments.back().motion = easeFn;
    }
    return *this;
  }

  //! Returns the number of seconds required to move through all Phrases.
  float getDuration() const { return _duration; }

  //! Returns the value at the end of the Sequence.
  T endValue() const { return _segments.empty() ? _initial_value : _segments.back()->getEndValue(); }

  //! Returns the value at the beginning of the Sequence.
  T initialValue() const { return _initial_value; }

private:
  std::vector<PhraseRef<T>>  _segments;
  T                       _initial_value;
  float                   _duration = 0.0f;

  friend class Timeline;
};

//! Returns the value of this sequence for a given point in time.
// Would be nice to have a constant-time check (without a while loop).
template<typename T>
T Sequence<T>::getValue( float atTime )
{
  if( atTime < 0.0f )
  {
    return _initial_value;
  }
  else if ( atTime >= _duration )
  {
    return endValue();
  }

  auto iter = _segments.begin();
  while( iter < _segments.end() ) {
    if( (*iter)->getEndTime() > atTime )
    {
      return (*iter)->getValue( atTime );
    }
    ++iter;
  }
  // past the end, get the final value
  // this should be unreachable, given that we return early if time >= duration
  return endValue();
}

template<typename T>
using SequenceRef = std::shared_ptr<Sequence<T>>;

} // namespace choreograph
<commit_msg>Remove ease functors for anonymous namespaces fns.<commit_after>/*
 * Copyright (c) 2014 David Wicks, sansumbrella.com
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or
 * without modification, are permitted provided that the following
 * conditions are met:
 *
 * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

 #pragma once

namespace choreograph
{

// A motion describes a one-dimensional change through time.
// These can be ease equations, always return the same value, or sample some data. Whatever you want.
// For classic motion, EaseFn( 0 ) = 0, EaseFn( 1 ) = 1.
// set( 0.0f ).move( )
// then again, might just make a curve struct that is callable and have a way to set its initial and final derivatives.
typedef std::function<float (float)> EaseFn;

namespace
{
//! Default ease function for holds. Keeps constant value output.
inline float easeHold( float t ) { return 0.0f; }
//! Default ease function for ramps.
inline float easeNone( float t ) { return t; }

} // namespace

//! A Position describes a point in time.
template<typename T>
struct Position
{
  Position() = default;

  Position( const T &value, float time = 0.0f ):
    time( time ),
    value( value )
  {}

  float time = 0.0f;
  T     value;
};

//! The default templated lerp function.
template<typename T>
T lerpT( const T &a, const T &b, float t )
{
  return a + (b - a) * t;
}

/**
 A Phrase is a part of a Sequence.
 It describes the motion between two positions.
 This is the essence of a Tween, with all values held internally.
 */
template<typename T>
class Phrase
{
public:
  using LerpFn = std::function<T (const T&, const T&, float)>;

  Phrase( const T &end, float duration, const EaseFn &easeFn = &easeNone ):
    end( end, duration ),
    motion( easeFn )
  {}

  Phrase( const Position<T> &start, const Position<T> &end, const EaseFn &easeFn = &easeNone ):
    start( start ),
    end( end ),
    motion( easeFn )
  {}

  virtual ~Phrase() = default;

  //! Set start time to \a time. End time is adjusted to preserve duration.
  void shiftStartTimeTo( float time ) {
    float delta = time - start.time;
    start.time = time;
    end.time += delta;
  }

  //! Change start value to \a value.
  void setStartValue( const T &value )
  {
    start.value = value;
  }

  //! Returns the value at the beginning of this phrase.
  inline const T& getStartValue() const { return start.value; }
  //! Returns the value at the end of this phrase.
  inline const T& getEndValue() const { return end.value; }

  //! Returns the time at the beginning of this phrase.
  inline float getStartTime() const { return start.time; }
  //! Returns the time at the end of this phrase.
  inline float getEndTime() const { return end.time; }

  //! Returns normalized time if t is in range [start.time, end.time].
  inline float normalizeTime( float t ) const { return (t - start.time) / (end.time - start.time); }
  //! Returns the duration of this phrase.
  inline float getDuration() const { return end.time - start.time; }

  //! Returns the interpolated value at the given time.
  virtual T getValue( float atTime ) const
  {
    return lerpFn( start.value, end.value, motion( normalizeTime( atTime ) ) );
  }
private:
  Position<T> start;
  Position<T> end;
  EaseFn      motion;
  LerpFn      lerpFn = &lerpT<T>;
};

template<typename T>
using PhraseRef = std::shared_ptr<Phrase<T>>;

/**
 Phrase with separately-interpolated components.
 Allows for the use of separate ease functions per component.
 All components must be of the same type.
 */
template<typename T>
class Phrase2 : public Phrase<T>
{
public:
  Phrase2( const T &end, float duration, const EaseFn &ease_x, const EaseFn &ease_y ):
    Phrase<T>( end, duration ),
    motion_x( ease_x ),
    motion_y( ease_y )
  {}

  Phrase2( const Position<T> &start, const Position<T> &end, const EaseFn &ease_x, const EaseFn &ease_y ):
    Phrase<T>( start, end ),
    motion_x( ease_x ),
    motion_y( ease_y )
  {}

  //! Returns the interpolated value at the given time.
  T getValue( float atTime ) const override
  {
    float t = Phrase<T>::normalizeTime( atTime );
    return T( componentLerpFn( Phrase<T>::getStartValue().x, Phrase<T>::getEndValue().x, motion_x( t ) ),
             componentLerpFn( Phrase<T>::getStartValue().y, Phrase<T>::getEndValue().y, motion_y( t ) ) );
  }

  //! Set ease functions for first and second components.
  void setEase( const EaseFn &component_0, const EaseFn &component_1 ) {
    motion_x = component_0;
    motion_y = component_1;
  }

private:
  using ComponentT = decltype( T().x ); // get the type of the x component;
  using ComponentLerpFn = std::function<ComponentT (const ComponentT&, const ComponentT&, float)>;

  ComponentLerpFn componentLerpFn = &lerpT<ComponentT>;
  EaseFn          motion_x;
  EaseFn          motion_y;
};

using Phrase2v = Phrase2<ci::vec2>;

/**
 A Sequence of motions.
 Our essential compositional tool, describing all the transformations to one element.
 A kind of platonic idea of an animation sequence; this describes a motion without giving it an output.
*/
template<typename T>
class Sequence
{
public:
  // Sequences always need to have some valid value.
  Sequence() = delete;

  //! Construct a Sequence with an initial \a value.
  explicit Sequence( const T &value ):
    _initial_value( value )
  {}

  //! Returns the Sequence value at \a atTime.
  T getValue( float atTime );

  //! Returns the Sequence value at \a time, looping past the end from inflection point to the end.
  T getValueWrapped( float time, float inflectionPoint = 0.0f ) const
  {
    if( time > getDuration() ) {
      return getValue( inflectionPoint + std::fmodf( time, getDuration() - inflectionPoint ) );
    }
    else {
      return getValue( time );
    }
  }

  //! Set current value. An instantaneous hold.
  Sequence<T>& set( const T &value )
  {
    if( _segments.empty() ) {
      _initial_value = value;
    }
    else {
      hold( value, 0.0f );
    }
    return *this;
  }

  //! Returns a copy of this sequence. Useful if you want to make a base animation and modify that.
  std::shared_ptr<Sequence<T>> copy() const { return std::make_shared<Sequence<T>>( *this ); }

  //! Hold on current end value for \a duration seconds.
  Sequence<T>& wait( float duration ) { return hold( duration ); }

  //! Hold on current end value for \a duration seconds.
  Sequence<T>& hold( float duration )
  {
    return hold( endValue(), duration );
  }

  //! Hold on \a value for \a duration seconds.
  Sequence<T>& hold( const T &value, float duration )
  {
    Position<T> start{ value, _duration };
    Position<T> end{ value, _duration + duration };
    auto phrase = std::make_shared<Phrase<T>>( start, end, &easeHold );

    _segments.push_back( phrase );
    _duration = phrase->getEndTime();

    return *this;
  }

  //! Animate to \a value over \a duration seconds using \a ease easing.
  Sequence<T>& rampTo( const T &value, float duration, const EaseFn &ease = &easeNone )
  {
    Position<T> start{ endValue(), _duration };
    Position<T> end{ value, _duration + duration };
    auto phrase = std::make_shared<Phrase<T>>( start, end, ease );

    _segments.push_back( phrase );

    _duration = phrase->getEndTime();

    return *this;
  }

  template<typename PhraseT, typename... Args>
  Sequence<T>& then( const T& value, float duration, Args... args )
  {
    Position<T> start{ endValue(), _duration };
    Position<T> end{ value, _duration + duration };
    auto phrase = std::make_shared<PhraseT>( start, end, args... );
    // Would expect the following to make my dream user syntax work.
//    auto phrase = std::make_shared<PhraseT<T>>( start, end, args... );

    _segments.push_back( phrase );
    _duration = phrase->getEndTime();

    return *this;
  }

  Sequence<T>& then( const PhraseRef<T>& phrase )
  {
    phrase->setStartValue( endValue() );
    phrase->shiftStartTimeTo( _duration );
    _duration = phrase->getEndTime();

    _segments.push_back( phrase );

    return *this;
  }

  //! Sets the ease function of the last Phrase in the Sequence.
  Sequence<T>& ease( const EaseFn &easeFn ) {
    if( ! _segments.empty() ) {
      _segments.back().motion = easeFn;
    }
    return *this;
  }

  //! Returns the number of seconds required to move through all Phrases.
  float getDuration() const { return _duration; }

  //! Returns the value at the end of the Sequence.
  T endValue() const { return _segments.empty() ? _initial_value : _segments.back()->getEndValue(); }

  //! Returns the value at the beginning of the Sequence.
  T initialValue() const { return _initial_value; }

private:
  std::vector<PhraseRef<T>>  _segments;
  T                       _initial_value;
  float                   _duration = 0.0f;

  friend class Timeline;
};

//! Returns the value of this sequence for a given point in time.
// Would be nice to have a constant-time check (without a while loop).
template<typename T>
T Sequence<T>::getValue( float atTime )
{
  if( atTime < 0.0f )
  {
    return _initial_value;
  }
  else if ( atTime >= _duration )
  {
    return endValue();
  }

  auto iter = _segments.begin();
  while( iter < _segments.end() ) {
    if( (*iter)->getEndTime() > atTime )
    {
      return (*iter)->getValue( atTime );
    }
    ++iter;
  }
  // past the end, get the final value
  // this should be unreachable, given that we return early if time >= duration
  return endValue();
}

template<typename T>
using SequenceRef = std::shared_ptr<Sequence<T>>;

} // namespace choreograph
<|endoftext|>


<commit_before>#include <gloperate-qt/viewer/EventRoutingMapping.h>

#include <gloperate/ext-includes-begin.h>
#include <QTimer>
#include <QToolTip>
#include <gloperate/ext-includes-end.h>

#include <glbinding/gl/enum.h>

#include <gloperate/base/make_unique.hpp>
#include <gloperate/base/RenderTargetType.h>
#include <gloperate/painter/AbstractCameraCapability.h>
#include <gloperate/painter/AbstractProjectionCapability.h>
#include <gloperate/painter/AbstractViewportCapability.h>
#include <gloperate/painter/AbstractTargetFramebufferCapability.h>
#include <gloperate/painter/AbstractMetaInformationCapability.h>
#include <gloperate/painter/TypedRenderTargetCapability.h>
#include <gloperate/painter/Painter.h>
#include <gloperate/input/AbstractEvent.h>
#include <gloperate/input/KeyboardEvent.h>
#include <gloperate/input/MouseEvent.h>
#include <gloperate/input/WheelEvent.h>
#include <gloperate/navigation/WorldInHandNavigation.h>
#include <gloperate/navigation/CoordinateProvider.h>
#include <gloperate/tools/ObjectIdExtractor.h>

#include <gloperate-qt/viewer/QtOpenGLWindow.h>

using namespace gloperate;

using gloperate::make_unique;

namespace
{
    const int g_tooltipTimeout = 200;
}

namespace gloperate_qt
{

EventRoutingMapping::EventRoutingMapping(QtOpenGLWindow * window)
: AbstractQtMapping(window)
, m_viewportCapability(nullptr)
, m_typedRenderTargetCapability(nullptr)
{
}

EventRoutingMapping::~EventRoutingMapping()
{
}

void EventRoutingMapping::initializeTools()
{
    m_renderTarget = nullptr;
    m_viewportCapability = nullptr;
    m_typedRenderTargetCapability = nullptr;

    if (!m_painter)
    {
        return;
    }

    if (m_painter->supports<AbstractCameraCapability>() &&
        m_painter->supports<AbstractViewportCapability>() &&
        m_painter->supports<AbstractProjectionCapability>() &&
        (m_painter->supports<AbstractTypedRenderTargetCapability>() ||
         m_painter->supports<AbstractTargetFramebufferCapability>()))
    {
        auto cameraCapability = m_painter->getCapability<AbstractCameraCapability>();
        auto projectionCapability = m_painter->getCapability<AbstractProjectionCapability>();
        m_viewportCapability = m_painter->getCapability<AbstractViewportCapability>();

        m_typedRenderTargetCapability = m_painter->getCapability<TypedRenderTargetCapability>();
        if (!m_typedRenderTargetCapability)
        {
            m_renderTarget = make_unique<TypedRenderTargetCapability>();
            m_typedRenderTargetCapability = m_renderTarget.get();

            auto fboCapability = m_painter->getCapability<AbstractTargetFramebufferCapability>();
            fboCapability->changed.connect([this] () { this->onTargetFramebufferChanged(); });
        }
    }
    if (m_painter->supports<AbstractEventRoutingCapability>())
    {
        m_eventRoutingCapability = m_painter->getCapability<AbstractEventRoutingCapability>();
    }
    else
    {
        m_eventRoutingCapability = nullptr;
    }
}

void EventRoutingMapping::mapEvent(AbstractEvent * event)
{
    if (m_renderTarget && !m_renderTarget->hasRenderTarget(RenderTargetType::Depth))
        onTargetFramebufferChanged();

    switch (event->sourceType())
    {
    case EventType::Keyboard:
        mapKeyboardEvent(static_cast<KeyboardEvent *>(event));
        break;
    case EventType::Mouse:
        mapMouseEvent(static_cast<MouseEvent *>(event));
        break;
    case EventType::Wheel:
        mapWheelEvent(static_cast<WheelEvent *>(event));
        break;
    default:
        break;
    }
}

void EventRoutingMapping::mapKeyboardEvent(KeyboardEvent * event)
{
    if (event && event->type() == KeyboardEvent::Type::Press)
    {
        auto curReciever = mapEventToReciever(RoutingEventType::Keyboard, static_cast<int>(RoutingEventValue::Any));
        if (curReciever)
            curReciever->keyPress(KeyboardInteractionArgs{m_currentId, event->key(), event->scanCode(), event->modifiers() });
    }
}

void EventRoutingMapping::mapMouseEvent(MouseEvent * mouseEvent)
{
    if (!mouseEvent) return;
    if (mouseEvent->pos() != glm::ivec2())
        m_currentMousePosition =  mouseEvent->pos() * static_cast<int>(m_window->devicePixelRatio());
    m_currentId = ObjectIdExtractor(m_viewportCapability, m_typedRenderTargetCapability).get(m_currentMousePosition);

    if (mouseEvent->type() == MouseEvent::Type::Press)
    {
        MouseButton curButton = mouseEvent->button();
        //mark button as pressed
        pressedButtons.insert(curButton);
    }
    else if (mouseEvent->type() == MouseEvent::Type::Move)
    {
        // for every pressed button without reciever, ask for a drag-reciever
        for (auto btn : pressedButtons)
        {
            if (!m_eventReciever.count(btn))
            {
                auto curReciever = mapEventToReciever(RoutingEventType::MouseDrag, btn);
                if (curReciever)
                    m_eventReciever[btn] = InteractionData{ curReciever, m_currentMousePosition };
            }
        }
        handleMouseMoveEvent();
    }
    else if (mouseEvent->type() == MouseEvent::Type::Release)
    {
        MouseButton curButton = mouseEvent->button();
        //mark button as released
        pressedButtons.erase(curButton);

        if (!m_eventReciever.count(curButton)) {
            //if there is no (drag-)reciever yet, then ask for a click-reciever
            auto curReciever = mapEventToReciever(RoutingEventType::MouseClick, curButton);
            if (curReciever)
                m_eventReciever[curButton] = InteractionData{ curReciever, m_currentMousePosition };
        }

        handleMouseReleaseEvent(curButton);

        m_eventReciever.erase(curButton);
    }
}

void EventRoutingMapping::handleMouseMoveEvent()
{
    for (auto btn : pressedButtons) {
        if (!m_eventReciever.count(btn)) continue;
        InteractionData &reciever = m_eventReciever.at(btn);
        glm::ivec2 delta = m_currentMousePosition - m_eventReciever.at(btn).startPosition;
        if (reciever.state != DragStarted) {
            reciever.reciever->dragStart(MouseInteractionArgs{m_currentId, reciever.startPosition, btn });
            reciever.state = DragStarted;
        }
        auto hoverReciever = mapEventToReciever(RoutingEventType::Any, static_cast<int>(RoutingEventValue::Any));
        AbstractInteraction* hoverElement = reciever.reciever != hoverReciever ? hoverReciever : nullptr;
        reciever.reciever->dragDelta(MouseInteractionDragDeltaArgs{m_currentId, m_currentMousePosition, btn, hoverElement, delta, reciever.startPosition });
    }
}

void EventRoutingMapping::handleMouseReleaseEvent(MouseButton value)
{
    if (!m_eventReciever.count(value)) return;

    InteractionData curReciever = m_eventReciever[value];
    if (m_currentMousePosition == curReciever.startPosition)
    {
        curReciever.reciever->click(MouseInteractionArgs{m_currentId, m_currentMousePosition, static_cast<MouseButton>(value) });
    }
    else
    {
        curReciever.reciever->dragEnd(MouseInteractionArgs{m_currentId, m_currentMousePosition, static_cast<MouseButton>(value) });
    }
}

void EventRoutingMapping::mapWheelEvent(WheelEvent * wheelEvent)
{
    m_currentMousePosition = wheelEvent->pos() * static_cast<int>(m_window->devicePixelRatio());
    auto reciever = mapEventToReciever(RoutingEventType::MouseWheel, static_cast<int>(RoutingEventValue::Any));
    if (reciever) {
        reciever->mouseWheel(MouseInteractionWheelArgs{m_currentId, m_currentMousePosition, wheelEvent->angleDelta() });
    }
}

void EventRoutingMapping::onTargetFramebufferChanged()
{
    auto fbo = m_painter->getCapability<AbstractTargetFramebufferCapability>()->framebuffer();
    if (!fbo)
    {
        fbo = globjects::Framebuffer::defaultFBO();
    }

    m_renderTarget->setRenderTarget(RenderTargetType::Depth, fbo,
        gl::GL_DEPTH_ATTACHMENT, gl::GL_DEPTH_COMPONENT);
}

AbstractInteraction* EventRoutingMapping::mapEventToReciever(RoutingEventType eventType, int eventValue) const
{
    if (!m_eventRoutingCapability)
        return nullptr;
    // request registered eventReciever
    AbstractInteraction* curReciever = m_eventRoutingCapability->mapEventToReciever(m_currentId, eventType, eventValue);

    if (!curReciever)
        // forward event to navigation
        curReciever = m_eventRoutingCapability->getNavigation();
    return curReciever;
}
} // namespace gloperate_qt
<commit_msg>implement feedback<commit_after>#include <gloperate-qt/viewer/EventRoutingMapping.h>

#include <gloperate/ext-includes-begin.h>
#include <QTimer>
#include <QToolTip>
#include <gloperate/ext-includes-end.h>

#include <glbinding/gl/enum.h>

#include <gloperate/base/make_unique.hpp>
#include <gloperate/base/RenderTargetType.h>
#include <gloperate/painter/AbstractCameraCapability.h>
#include <gloperate/painter/AbstractProjectionCapability.h>
#include <gloperate/painter/AbstractViewportCapability.h>
#include <gloperate/painter/AbstractTargetFramebufferCapability.h>
#include <gloperate/painter/AbstractMetaInformationCapability.h>
#include <gloperate/painter/TypedRenderTargetCapability.h>
#include <gloperate/painter/Painter.h>
#include <gloperate/input/AbstractEvent.h>
#include <gloperate/input/KeyboardEvent.h>
#include <gloperate/input/MouseEvent.h>
#include <gloperate/input/WheelEvent.h>
#include <gloperate/navigation/WorldInHandNavigation.h>
#include <gloperate/navigation/CoordinateProvider.h>
#include <gloperate/tools/ObjectIdExtractor.h>

#include <gloperate-qt/viewer/QtOpenGLWindow.h>

using namespace gloperate;

using gloperate::make_unique;

namespace
{
    const int g_tooltipTimeout = 200;
}

namespace gloperate_qt
{

EventRoutingMapping::EventRoutingMapping(QtOpenGLWindow * window)
: AbstractQtMapping(window)
, m_viewportCapability(nullptr)
, m_typedRenderTargetCapability(nullptr)
{
}

EventRoutingMapping::~EventRoutingMapping()
{
}

void EventRoutingMapping::initializeTools()
{
    m_renderTarget = nullptr;
    m_viewportCapability = nullptr;
    m_typedRenderTargetCapability = nullptr;

    if (!m_painter)
    {
        return;
    }

    if (m_painter->supports<AbstractCameraCapability>() &&
        m_painter->supports<AbstractViewportCapability>() &&
        m_painter->supports<AbstractProjectionCapability>() &&
        (m_painter->supports<AbstractTypedRenderTargetCapability>() ||
         m_painter->supports<AbstractTargetFramebufferCapability>()))
    {
        auto cameraCapability = m_painter->getCapability<AbstractCameraCapability>();
        auto projectionCapability = m_painter->getCapability<AbstractProjectionCapability>();
        m_viewportCapability = m_painter->getCapability<AbstractViewportCapability>();

        m_typedRenderTargetCapability = m_painter->getCapability<TypedRenderTargetCapability>();
        if (!m_typedRenderTargetCapability)
        {
            m_renderTarget = make_unique<TypedRenderTargetCapability>();
            m_typedRenderTargetCapability = m_renderTarget.get();

            auto fboCapability = m_painter->getCapability<AbstractTargetFramebufferCapability>();
            fboCapability->changed.connect([this] () { this->onTargetFramebufferChanged(); });
        }
    }
    if (m_painter->supports<AbstractEventRoutingCapability>())
    {
        m_eventRoutingCapability = m_painter->getCapability<AbstractEventRoutingCapability>();
    }
    else
    {
        m_eventRoutingCapability = nullptr;
    }
}

void EventRoutingMapping::mapEvent(AbstractEvent * event)
{
    if (m_renderTarget && !m_renderTarget->hasRenderTarget(RenderTargetType::Depth))
        onTargetFramebufferChanged();

    switch (event->sourceType())
    {
    case EventType::Keyboard:
        mapKeyboardEvent(static_cast<KeyboardEvent *>(event));
        break;
    case EventType::Mouse:
        mapMouseEvent(static_cast<MouseEvent *>(event));
        break;
    case EventType::Wheel:
        mapWheelEvent(static_cast<WheelEvent *>(event));
        break;
    default:
        break;
    }
}

void EventRoutingMapping::mapKeyboardEvent(KeyboardEvent * event)
{
    if (event && event->type() == KeyboardEvent::Type::Press)
    {
        auto curReciever = mapEventToReciever(RoutingEventType::Keyboard, static_cast<int>(RoutingEventValue::Any));
        if (curReciever)
            curReciever->keyPress(KeyboardInteractionArgs{m_currentId, event->key(), event->scanCode(), event->modifiers() });
    }
}

void EventRoutingMapping::mapMouseEvent(MouseEvent * mouseEvent)
{
    if (!mouseEvent) return;

    if (mouseEvent->pos() != glm::ivec2())
        m_currentMousePosition =  mouseEvent->pos() * static_cast<int>(m_window->devicePixelRatio());

    m_currentId = ObjectIdExtractor(m_viewportCapability, m_typedRenderTargetCapability).get(m_currentMousePosition);

    if (mouseEvent->type() == MouseEvent::Type::Press)
    {
        MouseButton curButton = mouseEvent->button();
        //mark button as pressed
        pressedButtons.insert(curButton);
    }
    else if (mouseEvent->type() == MouseEvent::Type::Move)
    {
        // for every pressed button without reciever, ask for a drag-reciever
        for (auto btn : pressedButtons)
        {
            if (!m_eventReciever.count(btn))
            {
                auto curReciever = mapEventToReciever(RoutingEventType::MouseDrag, btn);
                if (curReciever)
                    m_eventReciever[btn] = InteractionData{ curReciever, m_currentMousePosition };
            }
        }
        handleMouseMoveEvent();
    }
    else if (mouseEvent->type() == MouseEvent::Type::Release)
    {
        MouseButton curButton = mouseEvent->button();
        //mark button as released
        pressedButtons.erase(curButton);

        if (!m_eventReciever.count(curButton)) {
            //if there is no (drag-)reciever yet, then ask for a click-reciever
            auto curReciever = mapEventToReciever(RoutingEventType::MouseClick, curButton);
            if (curReciever)
                m_eventReciever[curButton] = InteractionData{ curReciever, m_currentMousePosition };
        }

        handleMouseReleaseEvent(curButton);

        m_eventReciever.erase(curButton);
    }
}

void EventRoutingMapping::handleMouseMoveEvent()
{
    for (auto btn : pressedButtons) {
        if (!m_eventReciever.count(btn)) continue;
        InteractionData &reciever = m_eventReciever.at(btn);
        glm::ivec2 delta = m_currentMousePosition - m_eventReciever.at(btn).startPosition;
        if (reciever.state != DragStarted) {
            reciever.reciever->dragStart(MouseInteractionArgs{m_currentId, reciever.startPosition, btn });
            reciever.state = DragStarted;
        }
        auto hoverReciever = mapEventToReciever(RoutingEventType::Any, static_cast<int>(RoutingEventValue::Any));
        AbstractInteraction* hoverElement = reciever.reciever != hoverReciever ? hoverReciever : nullptr;
        reciever.reciever->dragDelta(MouseInteractionDragDeltaArgs{m_currentId, m_currentMousePosition, btn, hoverElement, delta, reciever.startPosition });
    }
}

void EventRoutingMapping::handleMouseReleaseEvent(MouseButton value)
{
    if (!m_eventReciever.count(value)) return;

    InteractionData curReciever = m_eventReciever[value];
    if (m_currentMousePosition == curReciever.startPosition)
    {
        curReciever.reciever->click(MouseInteractionArgs{m_currentId, m_currentMousePosition, static_cast<MouseButton>(value) });
    }
    else
    {
        curReciever.reciever->dragEnd(MouseInteractionArgs{m_currentId, m_currentMousePosition, static_cast<MouseButton>(value) });
    }
}

void EventRoutingMapping::mapWheelEvent(WheelEvent * wheelEvent)
{
    m_currentMousePosition = wheelEvent->pos() * static_cast<int>(m_window->devicePixelRatio());
    auto reciever = mapEventToReciever(RoutingEventType::MouseWheel, static_cast<int>(RoutingEventValue::Any));
    if (reciever) {
        reciever->mouseWheel(MouseInteractionWheelArgs{m_currentId, m_currentMousePosition, wheelEvent->angleDelta() });
    }
}

void EventRoutingMapping::onTargetFramebufferChanged()
{
    auto fbo = m_painter->getCapability<AbstractTargetFramebufferCapability>()->framebuffer();
    if (!fbo)
    {
        fbo = globjects::Framebuffer::defaultFBO();
    }

    m_renderTarget->setRenderTarget(RenderTargetType::Depth, fbo,
        gl::GL_DEPTH_ATTACHMENT, gl::GL_DEPTH_COMPONENT);
}

AbstractInteraction* EventRoutingMapping::mapEventToReciever(RoutingEventType eventType, int eventValue) const
{
    if (!m_eventRoutingCapability)
        return nullptr;
    // request registered eventReciever
    AbstractInteraction* curReciever = m_eventRoutingCapability->mapEventToReciever(m_currentId, eventType, eventValue);

    if (!curReciever)
        // forward event to navigation
        curReciever = m_eventRoutingCapability->getNavigation();
    return curReciever;
}
} // namespace gloperate_qt
<|endoftext|>


<commit_before>#include "net_statistics.h"
#include <boost/bind.hpp>
#include <boost/function.hpp>
#include "logging.h"

namespace baidu {
namespace shuttle {

const static int32_t sNetStatInterval = 5000; //5 seconds

NetStatistics::NetStatistics(const std::string& if_name) : if_name_(if_name) {
    send_speed_ = 0L;
    recv_speed_ = 0L;
    int64_t send_amount = -1;
    int64_t recv_amount = -1;
    bool ok = GetCurNetAmount(&send_amount, &recv_amount);
    if (!ok) {
        LOG(WARNING, "fail to get network statistics from /sys/class/net/%s", if_name.c_str());
        return;
    }
    pool_.DelayTask(sNetStatInterval, boost::bind(&NetStatistics::CheckStatistics, this, send_amount, recv_amount));
}

bool NetStatistics::GetCurNetAmount(int64_t* send_amount, int64_t* recv_amount) {
    std::string recv_bytes_file = std::string("/sys/class/net/") + if_name_ + "/statistics/rx_bytes";
    std::string send_bytes_file = std::string("/sys/class/net/") + if_name_ + "/statistics/tx_bytes";
    FILE* file = fopen(recv_bytes_file.c_str(), "r");
    if (file) {
        fscanf(file, "%lld", recv_amount);
        fclose(file);
    } else {
        return false;
    }
    file = fopen(send_bytes_file.c_str(), "r");
    if (file) {
        fscanf(file, "%lld", send_amount);
        fclose(file);
    } else {
        return false;
    }
    return true;
}

void NetStatistics::CheckStatistics(int64_t last_send_amount, int64_t last_recv_amount) {
    int64_t send_amount = -1;
    int64_t recv_amount = -1;
    GetCurNetAmount(&send_amount, &recv_amount);
    {
        MutexLock lock(&mu_);
        send_speed_ = (send_amount - last_send_amount) / (sNetStatInterval / 1000);
        recv_speed_ = (recv_amount - last_recv_amount) / (sNetStatInterval / 1000);
    }
    pool_.DelayTask(sNetStatInterval, boost::bind(&NetStatistics::CheckStatistics, this, send_amount, recv_amount));
}

}
}

<commit_msg>avoid stdout pollution<commit_after>#include "net_statistics.h"
#include <stdio.h>
#include <boost/bind.hpp>
#include <boost/function.hpp>

namespace baidu {
namespace shuttle {

const static int32_t sNetStatInterval = 5000; //5 seconds

NetStatistics::NetStatistics(const std::string& if_name) : if_name_(if_name) {
    send_speed_ = 0L;
    recv_speed_ = 0L;
    int64_t send_amount = -1;
    int64_t recv_amount = -1;
    bool ok = GetCurNetAmount(&send_amount, &recv_amount);
    if (!ok) {
        fprintf(stderr, "fail to get network statistics from /sys/class/net/%s\n", if_name.c_str());
        return;
    }
    pool_.DelayTask(sNetStatInterval, boost::bind(&NetStatistics::CheckStatistics, this, send_amount, recv_amount));
}

bool NetStatistics::GetCurNetAmount(int64_t* send_amount, int64_t* recv_amount) {
    std::string recv_bytes_file = std::string("/sys/class/net/") + if_name_ + "/statistics/rx_bytes";
    std::string send_bytes_file = std::string("/sys/class/net/") + if_name_ + "/statistics/tx_bytes";
    FILE* file = fopen(recv_bytes_file.c_str(), "r");
    if (file) {
        fscanf(file, "%lld", recv_amount);
        fclose(file);
    } else {
        return false;
    }
    file = fopen(send_bytes_file.c_str(), "r");
    if (file) {
        fscanf(file, "%lld", send_amount);
        fclose(file);
    } else {
        return false;
    }
    return true;
}

void NetStatistics::CheckStatistics(int64_t last_send_amount, int64_t last_recv_amount) {
    int64_t send_amount = -1;
    int64_t recv_amount = -1;
    GetCurNetAmount(&send_amount, &recv_amount);
    {
        MutexLock lock(&mu_);
        send_speed_ = (send_amount - last_send_amount) / (sNetStatInterval / 1000);
        recv_speed_ = (recv_amount - last_recv_amount) / (sNetStatInterval / 1000);
    }
    pool_.DelayTask(sNetStatInterval, boost::bind(&NetStatistics::CheckStatistics, this, send_amount, recv_amount));
}

}
}

<|endoftext|>


<commit_before>/*----------------------------------------------------------------------------*/
/* Copyright (c) FIRST 2015. All Rights Reserved.                             */
/* Open Source Software - may be modified and shared by FRC teams. The code   */
/* must be accompanied by the FIRST BSD license file in the root directory of */
/* the project.                                                               */
/*----------------------------------------------------------------------------*/

#include "Dispatcher.h"

#include <algorithm>
#include <iterator>

#include "tcpsockets/TCPAcceptor.h"
#include "tcpsockets/TCPConnector.h"
#include "Log.h"

using namespace nt;

ATOMIC_STATIC_INIT(Dispatcher)

Dispatcher::Dispatcher()
    : m_server(false),
      m_do_flush(false),
      m_reconnect_proto_rev(0x0300),
      m_do_reconnect(true) {
  m_active = false;
  m_update_rate = 100;
}

Dispatcher::~Dispatcher() {
  Stop();
}

void Dispatcher::StartServer(const char* listen_address, unsigned int port) {
  {
    std::lock_guard<std::mutex> lock(m_user_mutex);
    if (m_active) return;
    m_active = true;
  }
  m_server = true;

  using namespace std::placeholders;
  Storage::GetInstance().SetOutgoing(
      std::bind(&Dispatcher::QueueOutgoing, this, _1, _2, _3), m_server);

  m_dispatch_thread = std::thread(&Dispatcher::DispatchThreadMain, this);
  m_clientserver_thread =
      std::thread(&Dispatcher::ServerThreadMain, this, listen_address, port);
}

void Dispatcher::StartClient(const char* server_name, unsigned int port) {
  {
    std::lock_guard<std::mutex> lock(m_user_mutex);
    if (m_active) return;
    m_active = true;
  }
  m_server = false;

  using namespace std::placeholders;
  Storage::GetInstance().SetOutgoing(
      std::bind(&Dispatcher::QueueOutgoing, this, _1, _2, _3), m_server);

  m_dispatch_thread = std::thread(&Dispatcher::DispatchThreadMain, this);
  m_clientserver_thread =
      std::thread(&Dispatcher::ClientThreadMain, this, server_name, port);
}

void Dispatcher::Stop() {
  {
    std::lock_guard<std::mutex> lock(m_user_mutex);
    m_active = false;

    // close all connections
    for (auto& conn : m_connections) conn.net->Stop();
  }

  // wake up dispatch thread with a flush
  m_flush_cv.notify_one();

  // wake up client thread with a reconnect
  ClientReconnect();

  // wake up server thread by shutting down the socket
  if (m_server_acceptor) m_server_acceptor->shutdown();

  // join threads
  if (m_dispatch_thread.joinable()) m_dispatch_thread.join();
  if (m_clientserver_thread.joinable()) m_clientserver_thread.join();
}

void Dispatcher::SetUpdateRate(double interval) {
  // don't allow update rates faster than 100 ms
  if (interval < 0.1)
    interval = 0.1;
  m_update_rate = static_cast<unsigned int>(interval * 1000);
}

void Dispatcher::SetIdentity(llvm::StringRef name) {
  std::lock_guard<std::mutex> lock(m_user_mutex);
  m_identity = name;
}

void Dispatcher::Flush() {
  auto now = std::chrono::steady_clock::now();
  {
    std::lock_guard<std::mutex> lock(m_flush_mutex);
    // don't allow flushes more often than every 100 ms
    if ((now - m_last_flush) < std::chrono::milliseconds(100))
      return;
    m_last_flush = now;
    m_do_flush = true;
  }
  m_flush_cv.notify_one();
}

void Dispatcher::DispatchThreadMain() {
  // local copy of active m_connections
  struct ConnectionRef {
    NetworkConnection* net;
    NetworkConnection::Outgoing outgoing;
  };
  std::vector<ConnectionRef> connections;

  auto timeout_time = std::chrono::steady_clock::now();
  int count = 0;
  std::unique_lock<std::mutex> flush_lock(m_flush_mutex);
  while (m_active) {
    // handle loop taking too long
    auto start = std::chrono::steady_clock::now();
    if (start > timeout_time)
      timeout_time = start;

    // wait for periodic or when flushed
    timeout_time += std::chrono::milliseconds(m_update_rate);
    m_reconnect_cv.wait_until(flush_lock, timeout_time,
                              [&] { return !m_active || m_do_flush; });
    m_do_flush = false;
    if (!m_active) break;  // in case we were woken up to terminate

    if (++count > 10) {
      DEBUG("dispatch running");
      count = 0;
    }

    // make a local copy of the connections list (so we don't hold the lock)
    connections.resize(0);
    {
      std::lock_guard<std::mutex> user_lock(m_user_mutex);
      bool reconnect = false;
      for (auto& conn : m_connections) {
        if (conn.net->state() == NetworkConnection::kActive) {
          connections.push_back(ConnectionRef());
          connections.back().net = conn.net.get();
          connections.back().outgoing.swap(conn.outgoing);
        }
        if (!m_server && conn.net->state() == NetworkConnection::kDead)
          reconnect = true;
      }
      // reconnect if we disconnected (and a reconnect is not in progress)
      if (reconnect && !m_do_reconnect) {
        m_do_reconnect = true;
        m_reconnect_cv.notify_one();
      }
    }

    // scan outgoing messages to remove unnecessary updates

    // send outgoing messages
    for (auto& conn : connections) {
      if (!conn.outgoing.empty())
        conn.net->outgoing().emplace(std::move(conn.outgoing));
    }
  }
}

void Dispatcher::QueueOutgoing(std::shared_ptr<Message> msg,
                               NetworkConnection* only,
                               NetworkConnection* except) {
  std::lock_guard<std::mutex> user_lock(m_user_mutex);
  for (auto& conn : m_connections) {
    if (conn.net.get() == except) continue;
    if (only && conn.net.get() != only) continue;
    if (conn.net->state() != NetworkConnection::kDead)
      conn.outgoing.push_back(msg);
  }
}

void Dispatcher::ServerThreadMain(const char* listen_address,
                                  unsigned int port) {
  m_server_acceptor.reset(
      new TCPAcceptor(static_cast<int>(port), listen_address));
  if (m_server_acceptor->start() != 0) {
    m_active = false;
    return;
  }
  while (m_active) {
    auto stream = m_server_acceptor->accept();
    if (!stream) {
      m_active = false;
      break;
    }
    DEBUG("server got a connection");

    // add to connections list
    using namespace std::placeholders;
    Storage& storage = Storage::GetInstance();
    std::unique_ptr<NetworkConnection> conn_unique(new NetworkConnection(
        std::move(stream),
        std::bind(&Dispatcher::ServerHandshake, this, _1, _2, _3),
        std::bind(&Storage::GetEntryType, &storage, _1),
        std::bind(&Storage::ProcessIncoming, &storage, _1, _2, _3)));
    auto conn = conn_unique.get();
    {
      std::lock_guard<std::mutex> lock(m_user_mutex);
      m_connections.emplace_back(std::move(conn_unique));
    }
    conn->Start();
  }
}

void Dispatcher::ClientThreadMain(const char* server_name, unsigned int port) {
  while (m_active) {
    // sleep between retries
    std::this_thread::sleep_for(std::chrono::milliseconds(500));

    // try to connect (with timeout)
    DEBUG("client trying to connect");
    auto stream = TCPConnector::connect(server_name, static_cast<int>(port), 1);
    if (!stream) continue;  // keep retrying
    DEBUG("client connected");

    std::unique_lock<std::mutex> lock(m_user_mutex);
    using namespace std::placeholders;
    Storage& storage = Storage::GetInstance();
    std::unique_ptr<NetworkConnection> conn_unique(new NetworkConnection(
        std::move(stream),
        std::bind(&Dispatcher::ClientHandshake, this, _1, _2, _3),
        std::bind(&Storage::GetEntryType, &storage, _1),
        std::bind(&Storage::ProcessIncoming, &storage, _1, _2, _3)));
    auto conn = conn_unique.get();
    m_connections.resize(0);  // disconnect any current
    m_connections.emplace_back(std::move(conn_unique));
    conn->set_proto_rev(m_reconnect_proto_rev);
    conn->Start();

    // block until told to reconnect
    m_do_reconnect = false;
    m_reconnect_cv.wait(lock, [&] { return m_do_reconnect; });
  }
}

bool Dispatcher::ClientHandshake(
    NetworkConnection& conn,
    std::function<std::shared_ptr<Message>()> get_msg,
    std::function<void(llvm::ArrayRef<std::shared_ptr<Message>>)> send_msgs) {
  // get identity
  std::string self_id;
  {
    std::lock_guard<std::mutex> lock(m_user_mutex);
    self_id = m_identity;
  }

  // send client hello
  DEBUG("client: sending hello");
  send_msgs(Message::ClientHello(self_id));

  // wait for response
  auto msg = get_msg();
  if (!msg) {
    // disconnected, retry
    DEBUG("client: server disconnected before first response");
    return false;
  }

  if (msg->Is(Message::kProtoUnsup)) {
    if (msg->id() == 0x0200) ClientReconnect(0x0200);
    return false;
  }

  bool new_server = true;
  if (conn.proto_rev() >= 0x0300) {
    // should be server hello; if not, disconnect.
    if (!msg->Is(Message::kServerHello)) return false;
    conn.set_remote_id(msg->str());
    if ((msg->flags() & 1) != 0) new_server = false;
    // get the next message
    msg = get_msg();
  }

  // receive initial assignments
  std::vector<std::shared_ptr<Message>> incoming;
  for (;;) {
    if (!msg) {
      // disconnected, retry
      DEBUG("client: server disconnected during initial entries");
      return false;
    }
    if (msg->Is(Message::kServerHelloDone)) break;
    if (!msg->Is(Message::kEntryAssign)) {
      // unexpected message
      DEBUG("client: received message (" << msg->type() << ") other than entry assignment during initial handshake");
      return false;
    }
    incoming.emplace_back(std::move(msg));
    // get the next message
    msg = get_msg();
  }

  // generate outgoing assignments
  NetworkConnection::Outgoing outgoing;

  Storage::GetInstance().ApplyInitialAssignments(incoming, new_server,
                                                 conn.proto_rev(), &outgoing);

  if (conn.proto_rev() >= 0x0300)
    outgoing.emplace_back(Message::ClientHelloDone());

  if (!outgoing.empty()) send_msgs(outgoing);

  return true;
}

bool Dispatcher::ServerHandshake(
    NetworkConnection& conn,
    std::function<std::shared_ptr<Message>()> get_msg,
    std::function<void(llvm::ArrayRef<std::shared_ptr<Message>>)> send_msgs) {
  // Wait for the client to send us a hello.
  auto msg = get_msg();
  if (!msg) {
    DEBUG("server: client disconnected before sending hello");
    return false;
  }
  if (!msg->Is(Message::kClientHello)) {
    DEBUG("server: client initial message was not client hello");
    return false;
  }

  // Check that the client requested version is not too high.
  unsigned int proto_rev = msg->id();
  if (proto_rev > 0x0300) {
    DEBUG("server: client requested proto > 0x0300");
    send_msgs(Message::ProtoUnsup());
    return false;
  }

  if (proto_rev >= 0x0300) conn.set_remote_id(msg->str());

  // Set the proto version to the client requested version
  conn.set_proto_rev(proto_rev);

  // Send initial set of assignments
  NetworkConnection::Outgoing outgoing;

  // Start with server hello.  TODO: initial connection flag
  if (proto_rev >= 0x0300) {
    std::lock_guard<std::mutex> lock(m_user_mutex);
    outgoing.emplace_back(Message::ServerHello(0u, m_identity));
  }

  // Get snapshot of initial assignments
  Storage::GetInstance().GetInitialAssignments(&outgoing);

  // Finish with server hello done
  outgoing.emplace_back(Message::ServerHelloDone());

  // Batch transmit
  DEBUG("server: sending initial assignments");
  send_msgs(outgoing);

  // In proto rev 3.0 and later, the handshake concludes with a client hello
  // done message, so we can batch the assigns before marking the connection
  // active.  In pre-3.0, we need to just immediately mark it active and hand
  // off control to the dispatcher to assign them as they arrive.
  if (proto_rev >= 0x0300) {
    // receive client initial assignments
    std::vector<std::shared_ptr<Message>> incoming;
    msg = get_msg();
    for (;;) {
      if (!msg) {
        // disconnected, retry
        DEBUG("server: disconnected waiting for initial entries");
        return false;
      }
      if (msg->Is(Message::kClientHelloDone)) break;
      if (!msg->Is(Message::kEntryAssign)) {
        // unexpected message
        DEBUG("server: received message (" << msg->type() << ") other than entry assignment during initial handshake");
        return false;
      }
      incoming.push_back(msg);
      // get the next message (blocks)
      msg = get_msg();
    }
    Storage& storage = Storage::GetInstance();
    for (auto& msg : incoming) storage.ProcessIncoming(msg, &conn, proto_rev);
  }

  return true;
}

void Dispatcher::ClientReconnect(unsigned int proto_rev) {
  if (m_server) return;
  {
    std::lock_guard<std::mutex> lock(m_user_mutex);
    m_reconnect_proto_rev = proto_rev;
    m_do_reconnect = true;
  }
  m_reconnect_cv.notify_one();
}
<commit_msg>Dispatcher: Provide some INFO-level connection messages.<commit_after>/*----------------------------------------------------------------------------*/
/* Copyright (c) FIRST 2015. All Rights Reserved.                             */
/* Open Source Software - may be modified and shared by FRC teams. The code   */
/* must be accompanied by the FIRST BSD license file in the root directory of */
/* the project.                                                               */
/*----------------------------------------------------------------------------*/

#include "Dispatcher.h"

#include <algorithm>
#include <iterator>

#include "tcpsockets/TCPAcceptor.h"
#include "tcpsockets/TCPConnector.h"
#include "Log.h"

using namespace nt;

ATOMIC_STATIC_INIT(Dispatcher)

Dispatcher::Dispatcher()
    : m_server(false),
      m_do_flush(false),
      m_reconnect_proto_rev(0x0300),
      m_do_reconnect(true) {
  m_active = false;
  m_update_rate = 100;
}

Dispatcher::~Dispatcher() {
  Stop();
}

void Dispatcher::StartServer(const char* listen_address, unsigned int port) {
  {
    std::lock_guard<std::mutex> lock(m_user_mutex);
    if (m_active) return;
    m_active = true;
  }
  m_server = true;

  using namespace std::placeholders;
  Storage::GetInstance().SetOutgoing(
      std::bind(&Dispatcher::QueueOutgoing, this, _1, _2, _3), m_server);

  m_dispatch_thread = std::thread(&Dispatcher::DispatchThreadMain, this);
  m_clientserver_thread =
      std::thread(&Dispatcher::ServerThreadMain, this, listen_address, port);
}

void Dispatcher::StartClient(const char* server_name, unsigned int port) {
  {
    std::lock_guard<std::mutex> lock(m_user_mutex);
    if (m_active) return;
    m_active = true;
  }
  m_server = false;

  using namespace std::placeholders;
  Storage::GetInstance().SetOutgoing(
      std::bind(&Dispatcher::QueueOutgoing, this, _1, _2, _3), m_server);

  m_dispatch_thread = std::thread(&Dispatcher::DispatchThreadMain, this);
  m_clientserver_thread =
      std::thread(&Dispatcher::ClientThreadMain, this, server_name, port);
}

void Dispatcher::Stop() {
  {
    std::lock_guard<std::mutex> lock(m_user_mutex);
    m_active = false;

    // close all connections
    for (auto& conn : m_connections) conn.net->Stop();
  }

  // wake up dispatch thread with a flush
  m_flush_cv.notify_one();

  // wake up client thread with a reconnect
  ClientReconnect();

  // wake up server thread by shutting down the socket
  if (m_server_acceptor) m_server_acceptor->shutdown();

  // join threads
  if (m_dispatch_thread.joinable()) m_dispatch_thread.join();
  if (m_clientserver_thread.joinable()) m_clientserver_thread.join();
}

void Dispatcher::SetUpdateRate(double interval) {
  // don't allow update rates faster than 100 ms
  if (interval < 0.1)
    interval = 0.1;
  m_update_rate = static_cast<unsigned int>(interval * 1000);
}

void Dispatcher::SetIdentity(llvm::StringRef name) {
  std::lock_guard<std::mutex> lock(m_user_mutex);
  m_identity = name;
}

void Dispatcher::Flush() {
  auto now = std::chrono::steady_clock::now();
  {
    std::lock_guard<std::mutex> lock(m_flush_mutex);
    // don't allow flushes more often than every 100 ms
    if ((now - m_last_flush) < std::chrono::milliseconds(100))
      return;
    m_last_flush = now;
    m_do_flush = true;
  }
  m_flush_cv.notify_one();
}

void Dispatcher::DispatchThreadMain() {
  // local copy of active m_connections
  struct ConnectionRef {
    NetworkConnection* net;
    NetworkConnection::Outgoing outgoing;
  };
  std::vector<ConnectionRef> connections;

  auto timeout_time = std::chrono::steady_clock::now();
  int count = 0;
  std::unique_lock<std::mutex> flush_lock(m_flush_mutex);
  while (m_active) {
    // handle loop taking too long
    auto start = std::chrono::steady_clock::now();
    if (start > timeout_time)
      timeout_time = start;

    // wait for periodic or when flushed
    timeout_time += std::chrono::milliseconds(m_update_rate);
    m_reconnect_cv.wait_until(flush_lock, timeout_time,
                              [&] { return !m_active || m_do_flush; });
    m_do_flush = false;
    if (!m_active) break;  // in case we were woken up to terminate

    if (++count > 10) {
      DEBUG("dispatch running");
      count = 0;
    }

    // make a local copy of the connections list (so we don't hold the lock)
    connections.resize(0);
    {
      std::lock_guard<std::mutex> user_lock(m_user_mutex);
      bool reconnect = false;
      for (auto& conn : m_connections) {
        if (conn.net->state() == NetworkConnection::kActive) {
          connections.push_back(ConnectionRef());
          connections.back().net = conn.net.get();
          connections.back().outgoing.swap(conn.outgoing);
        }
        if (!m_server && conn.net->state() == NetworkConnection::kDead)
          reconnect = true;
      }
      // reconnect if we disconnected (and a reconnect is not in progress)
      if (reconnect && !m_do_reconnect) {
        m_do_reconnect = true;
        m_reconnect_cv.notify_one();
      }
    }

    // scan outgoing messages to remove unnecessary updates

    // send outgoing messages
    for (auto& conn : connections) {
      if (!conn.outgoing.empty())
        conn.net->outgoing().emplace(std::move(conn.outgoing));
    }
  }
}

void Dispatcher::QueueOutgoing(std::shared_ptr<Message> msg,
                               NetworkConnection* only,
                               NetworkConnection* except) {
  std::lock_guard<std::mutex> user_lock(m_user_mutex);
  for (auto& conn : m_connections) {
    if (conn.net.get() == except) continue;
    if (only && conn.net.get() != only) continue;
    if (conn.net->state() != NetworkConnection::kDead)
      conn.outgoing.push_back(msg);
  }
}

void Dispatcher::ServerThreadMain(const char* listen_address,
                                  unsigned int port) {
  m_server_acceptor.reset(
      new TCPAcceptor(static_cast<int>(port), listen_address));
  if (m_server_acceptor->start() != 0) {
    m_active = false;
    return;
  }
  while (m_active) {
    auto stream = m_server_acceptor->accept();
    if (!stream) {
      m_active = false;
      break;
    }
    DEBUG("server: client connection from " << stream->getPeerIP() << " port "
                                            << stream->getPeerPort());

    // add to connections list
    using namespace std::placeholders;
    Storage& storage = Storage::GetInstance();
    std::unique_ptr<NetworkConnection> conn_unique(new NetworkConnection(
        std::move(stream),
        std::bind(&Dispatcher::ServerHandshake, this, _1, _2, _3),
        std::bind(&Storage::GetEntryType, &storage, _1),
        std::bind(&Storage::ProcessIncoming, &storage, _1, _2, _3)));
    auto conn = conn_unique.get();
    {
      std::lock_guard<std::mutex> lock(m_user_mutex);
      m_connections.emplace_back(std::move(conn_unique));
    }
    conn->Start();
  }
}

void Dispatcher::ClientThreadMain(const char* server_name, unsigned int port) {
  while (m_active) {
    // sleep between retries
    std::this_thread::sleep_for(std::chrono::milliseconds(500));

    // try to connect (with timeout)
    DEBUG("client trying to connect");
    auto stream = TCPConnector::connect(server_name, static_cast<int>(port), 1);
    if (!stream) continue;  // keep retrying
    DEBUG("client connected");

    std::unique_lock<std::mutex> lock(m_user_mutex);
    using namespace std::placeholders;
    Storage& storage = Storage::GetInstance();
    std::unique_ptr<NetworkConnection> conn_unique(new NetworkConnection(
        std::move(stream),
        std::bind(&Dispatcher::ClientHandshake, this, _1, _2, _3),
        std::bind(&Storage::GetEntryType, &storage, _1),
        std::bind(&Storage::ProcessIncoming, &storage, _1, _2, _3)));
    auto conn = conn_unique.get();
    m_connections.resize(0);  // disconnect any current
    m_connections.emplace_back(std::move(conn_unique));
    conn->set_proto_rev(m_reconnect_proto_rev);
    conn->Start();

    // block until told to reconnect
    m_do_reconnect = false;
    m_reconnect_cv.wait(lock, [&] { return m_do_reconnect; });
  }
}

bool Dispatcher::ClientHandshake(
    NetworkConnection& conn,
    std::function<std::shared_ptr<Message>()> get_msg,
    std::function<void(llvm::ArrayRef<std::shared_ptr<Message>>)> send_msgs) {
  // get identity
  std::string self_id;
  {
    std::lock_guard<std::mutex> lock(m_user_mutex);
    self_id = m_identity;
  }

  // send client hello
  DEBUG("client: sending hello");
  send_msgs(Message::ClientHello(self_id));

  // wait for response
  auto msg = get_msg();
  if (!msg) {
    // disconnected, retry
    DEBUG("client: server disconnected before first response");
    return false;
  }

  if (msg->Is(Message::kProtoUnsup)) {
    if (msg->id() == 0x0200) ClientReconnect(0x0200);
    return false;
  }

  bool new_server = true;
  if (conn.proto_rev() >= 0x0300) {
    // should be server hello; if not, disconnect.
    if (!msg->Is(Message::kServerHello)) return false;
    conn.set_remote_id(msg->str());
    if ((msg->flags() & 1) != 0) new_server = false;
    // get the next message
    msg = get_msg();
  }

  // receive initial assignments
  std::vector<std::shared_ptr<Message>> incoming;
  for (;;) {
    if (!msg) {
      // disconnected, retry
      DEBUG("client: server disconnected during initial entries");
      return false;
    }
    if (msg->Is(Message::kServerHelloDone)) break;
    if (!msg->Is(Message::kEntryAssign)) {
      // unexpected message
      DEBUG("client: received message (" << msg->type() << ") other than entry assignment during initial handshake");
      return false;
    }
    incoming.emplace_back(std::move(msg));
    // get the next message
    msg = get_msg();
  }

  // generate outgoing assignments
  NetworkConnection::Outgoing outgoing;

  Storage::GetInstance().ApplyInitialAssignments(incoming, new_server,
                                                 conn.proto_rev(), &outgoing);

  if (conn.proto_rev() >= 0x0300)
    outgoing.emplace_back(Message::ClientHelloDone());

  if (!outgoing.empty()) send_msgs(outgoing);

  INFO("client: CONNECTED to server " << conn.stream().getPeerIP() << " port "
                                      << conn.stream().getPeerPort());
  return true;
}

bool Dispatcher::ServerHandshake(
    NetworkConnection& conn,
    std::function<std::shared_ptr<Message>()> get_msg,
    std::function<void(llvm::ArrayRef<std::shared_ptr<Message>>)> send_msgs) {
  // Wait for the client to send us a hello.
  auto msg = get_msg();
  if (!msg) {
    DEBUG("server: client disconnected before sending hello");
    return false;
  }
  if (!msg->Is(Message::kClientHello)) {
    DEBUG("server: client initial message was not client hello");
    return false;
  }

  // Check that the client requested version is not too high.
  unsigned int proto_rev = msg->id();
  if (proto_rev > 0x0300) {
    DEBUG("server: client requested proto > 0x0300");
    send_msgs(Message::ProtoUnsup());
    return false;
  }

  if (proto_rev >= 0x0300) conn.set_remote_id(msg->str());

  // Set the proto version to the client requested version
  conn.set_proto_rev(proto_rev);

  // Send initial set of assignments
  NetworkConnection::Outgoing outgoing;

  // Start with server hello.  TODO: initial connection flag
  if (proto_rev >= 0x0300) {
    std::lock_guard<std::mutex> lock(m_user_mutex);
    outgoing.emplace_back(Message::ServerHello(0u, m_identity));
  }

  // Get snapshot of initial assignments
  Storage::GetInstance().GetInitialAssignments(&outgoing);

  // Finish with server hello done
  outgoing.emplace_back(Message::ServerHelloDone());

  // Batch transmit
  DEBUG("server: sending initial assignments");
  send_msgs(outgoing);

  // In proto rev 3.0 and later, the handshake concludes with a client hello
  // done message, so we can batch the assigns before marking the connection
  // active.  In pre-3.0, we need to just immediately mark it active and hand
  // off control to the dispatcher to assign them as they arrive.
  if (proto_rev >= 0x0300) {
    // receive client initial assignments
    std::vector<std::shared_ptr<Message>> incoming;
    msg = get_msg();
    for (;;) {
      if (!msg) {
        // disconnected, retry
        DEBUG("server: disconnected waiting for initial entries");
        return false;
      }
      if (msg->Is(Message::kClientHelloDone)) break;
      if (!msg->Is(Message::kEntryAssign)) {
        // unexpected message
        DEBUG("server: received message (" << msg->type() << ") other than entry assignment during initial handshake");
        return false;
      }
      incoming.push_back(msg);
      // get the next message (blocks)
      msg = get_msg();
    }
    Storage& storage = Storage::GetInstance();
    for (auto& msg : incoming) storage.ProcessIncoming(msg, &conn, proto_rev);
  }

  INFO("server: client CONNECTED: " << conn.stream().getPeerIP() << " port "
                                    << conn.stream().getPeerPort());
  return true;
}

void Dispatcher::ClientReconnect(unsigned int proto_rev) {
  if (m_server) return;
  {
    std::lock_guard<std::mutex> lock(m_user_mutex);
    m_reconnect_proto_rev = proto_rev;
    m_do_reconnect = true;
  }
  m_reconnect_cv.notify_one();
}
<|endoftext|>


<commit_before>#include "pch.h"
#include "DspLimiter.h"

namespace SaneAudioRenderer
{
    namespace
    {
        const float slope = 1.0f - 1.0f / 20.0f; // 20:1 ratio
    }

    void DspLimiter::Initialize(uint32_t rate, uint32_t channels, bool exclusive)
    {
        m_exclusive = exclusive;
        m_rate = rate;
        m_channels = channels;

        m_active = false;
        m_holdWindow = 0;
        m_peak = 0.0f;
        m_threshold = 0.0f;
    }

    bool DspLimiter::Active()
    {
        return m_active;
    }

    void DspLimiter::Process(DspChunk& chunk)
    {
        if (chunk.IsEmpty())
            return;

        if (!m_exclusive ||
            chunk.GetFormat() != DspFormat::Float)
        {
            m_active = false;
            return;
        }

        m_active = true;

        auto data = reinterpret_cast<float*>(chunk.GetData());

        // Analyze samples
        float peak = 0.0f;
        for (size_t i = 0, n = chunk.GetSampleCount(); i < n; i++)
            peak = std::max(peak, std::abs(data[i]));

        // Configure limiter
        if (peak > 1.0f)
        {
            if (m_holdWindow <= 0)
            {
                NewTreshold(std::max(peak, 1.4f));
            }
            else if (peak > m_peak)
            {
                NewTreshold(peak);
            }

            m_holdWindow = m_rate * m_channels * 10; // 10 seconds
        }

        // Apply limiter
        if (m_holdWindow > 0)
        {
            for (size_t i = 0, n = chunk.GetSampleCount(); i < n; i++)
            {
                float& sample = data[i];
                const float absSample = std::abs(sample);

                if (absSample > m_threshold)
                    sample *= std::pow(m_threshold / absSample, slope);

                assert(std::abs(sample) <= 1.0f);
            }

            m_holdWindow -= chunk.GetSampleCount();
        }
    }

    void DspLimiter::Finish(DspChunk& chunk)
    {
        Process(chunk);
    }

    void DspLimiter::NewTreshold(float peak)
    {
        m_peak = peak;
        m_threshold = std::pow(1.0f / peak, 1.0f / slope - 1.0f) - 0.0001f;
        DebugOut("DspLimiter active with", m_peak, "peak and", m_threshold, "threshold");
    }
}
<commit_msg>Support double precision in DspLimiter<commit_after>#include "pch.h"
#include "DspLimiter.h"

namespace SaneAudioRenderer
{
    namespace
    {
        const float slope = 1.0f - 1.0f / 20.0f; // 20:1 ratio

        template <typename T>
        T GetPeak(const T* data, size_t n)
        {
            T peak = 0;

            for (size_t i = 0; i < n; i++)
                peak = std::max(peak, std::abs(data[i]));

            return peak;
        }

        template <typename T>
        void ApplyLimiter(T* data, size_t n, T threshold)
        {
            for (size_t i = 0; i < n; i++)
            {
                T& sample = data[i];
                const T absSample = std::abs(sample);

                if (absSample > threshold)
                    sample *= std::pow(threshold / absSample, slope);

                assert(std::abs(sample) <= 1);
            }
        }
    }

    void DspLimiter::Initialize(uint32_t rate, uint32_t channels, bool exclusive)
    {
        m_exclusive = exclusive;
        m_rate = rate;
        m_channels = channels;

        m_active = false;
        m_holdWindow = 0;
        m_peak = 0.0f;
        m_threshold = 0.0f;
    }

    bool DspLimiter::Active()
    {
        return m_active;
    }

    void DspLimiter::Process(DspChunk& chunk)
    {
        if (chunk.IsEmpty())
            return;

        if (!m_exclusive || (chunk.GetFormat() != DspFormat::Float &&
                             chunk.GetFormat() != DspFormat::Double))
        {
            m_active = false;
            return;
        }

        m_active = true;

        // Analyze samples
        float peak;
        if (chunk.GetFormat() == DspFormat::Double)
        {
            double largePeak = GetPeak((double*)chunk.GetData(), chunk.GetSampleCount());
            peak = std::nexttoward((float)largePeak, largePeak);
        }
        else
        {
            assert(chunk.GetFormat() == DspFormat::Float);
            peak = GetPeak((float*)chunk.GetData(), chunk.GetSampleCount());
        }

        // Configure limiter
        if (peak > 1.0f)
        {
            if (m_holdWindow <= 0)
            {
                NewTreshold(std::max(peak, 1.4f));
            }
            else if (peak > m_peak)
            {
                NewTreshold(peak);
            }

            m_holdWindow = m_rate * m_channels * 10; // 10 seconds
        }

        // Apply limiter
        if (m_holdWindow > 0)
        {
            if (chunk.GetFormat() == DspFormat::Double)
            {
                ApplyLimiter<double>((double*)chunk.GetData(), chunk.GetSampleCount(), m_threshold);
            }
            else
            {
                assert(chunk.GetFormat() == DspFormat::Float);
                ApplyLimiter((float*)chunk.GetData(), chunk.GetSampleCount(), m_threshold);
            }

            m_holdWindow -= chunk.GetSampleCount();
        }
    }

    void DspLimiter::Finish(DspChunk& chunk)
    {
        Process(chunk);
    }

    void DspLimiter::NewTreshold(float peak)
    {
        m_peak = peak;
        m_threshold = std::pow(1.0f / peak, 1.0f / slope - 1.0f) - 0.0001f;
        DebugOut("DspLimiter active with", m_peak, "peak and", m_threshold, "threshold");
    }
}
<|endoftext|>