void
NextEvent(MWEvent *event, int socket)
{
  fd_set	fdmask;
  XEvent	xev;
  static int nexttimeoutinitialized=0;
  static struct timeval nexttimeout;
  struct timeval timeout,oldtime,newtime;
  int	ret;
  char	c;

  if (!nexttimeoutinitialized)
    {
      nexttimeoutinitialized=1;
      nexttimeout.tv_sec=app_resources.time_interval/1000;
      nexttimeout.tv_usec=1000*(app_resources.time_interval%1000);
    }
  while (1)
    {
      icon_flash = (++icon_flash) % ICON_FLASH_PERIOD;

      if (!XPending(dpy))	/* this does an XFlush, too */
	if (flashIcon && !icon_flash && !mapped)
	  {
	    /* invert the icon  */
	    iconInverted = !iconInverted;
	    repaintIcon();
	  }

      /*
       * Look for events.  Try to arrange that X events have priority over
       * network traffic.  See if there's an X event pending.  If so, check
       * for a net event, too; if not, select on both the network and the X
       * connection.  If that doesn't time out, but there's no X event
       * pending, try again, just selecting on the X connection.  If that
       * times out, let the network event get processed.
       *
       * Can't just select on the two fds, because there may be X events
       * pending in the queue that have already been read.
       *
       * This may look baroque, but we've seen some instances where X server
       * latency seems to let the network events take priority over sever
       * events, leading to sluggish keyboard response and lots of local
       * death.
       *
       */

      if (!XPending(dpy))
	{
	  FD_ZERO(&fdmask);
	  FD_SET(displayFD, &fdmask);
	  FD_SET(socket, &fdmask);
	  for (ret=0;ret<=0;)
	    {
	      if ((nexttimeout.tv_sec<0)||
		  ((nexttimeout.tv_sec==0)&&(nexttimeout.tv_usec<=0)))
		{
		  nexttimeout.tv_sec=app_resources.time_interval/1000;
		  nexttimeout.tv_usec=
		    1000*(app_resources.time_interval%1000);
		  if (app_resources.robotic==TRUE)
		    {
		      event->eventType=RandomEvent();
		      return;
		    }
		}
	      timeout.tv_sec=nexttimeout.tv_sec;
	      timeout.tv_usec=nexttimeout.tv_usec;
	      gettimeofday(&oldtime,0);
	      ret=select(32,&fdmask,NULL,NULL,&timeout);
	      gettimeofday(&newtime,0);
	      nexttimeout.tv_sec-=newtime.tv_sec-oldtime.tv_sec;
	      nexttimeout.tv_usec-=newtime.tv_usec-oldtime.tv_usec;
	      if (nexttimeout.tv_usec<0)
		{
		  nexttimeout.tv_sec--;
		  nexttimeout.tv_usec+=1000000;
		}
	      if (ret==-1)
		{
		  if (errno!=EINTR)
		    MWError("select error on events");
		}
	      else if (ret==0)
		{
		  nexttimeout.tv_sec=app_resources.time_interval/1000;
		  nexttimeout.tv_usec=
		    1000*(app_resources.time_interval%1000);
		  if (app_resources.robotic==TRUE)
		    event->eventType=RandomEvent();
		  else
		    event->eventType=EVENT_TIMEOUT;
		  return;
		}
	    }
	}
      else
	{
	  FD_ZERO(&fdmask);
	  FD_SET(socket, &fdmask);
	  timeout.tv_sec = 0;
	  timeout.tv_usec = 0;
	  while ((ret = select(32, &fdmask, NULL, NULL, &timeout)) == -1)
	    if (errno != EINTR)
	      MWError("select error on events");
	}
      if (XPending(dpy))
	{
	  XNextEvent(dpy, &xev);
	  switch (xev.type)
	    {
	    case KeyPress:
	      event->eventType = 0;
	      XLookupString((XKeyEvent *) &xev, &c, 1,
			    NULL, NULL);

	      switch(c)
		{
		case 'a':
		case '4':	/* keypad */
		  event->eventType = EVENT_A;
		  return;

		case 's':
		case '5':
		  event->eventType = EVENT_S;
		  return;

		case 'd':
		case '6':
		  event->eventType = EVENT_D;
		  return;

		case 'f':
		case ',':
		  event->eventType = EVENT_F;
		  return;

		case ' ':
		case '\033':	/* ESC lead in of arrow */
		  event->eventType = EVENT_BAR;
		  return;

		case 'q':
		case '\177':	/* DEL */
		case '\003':	/* ^C */
		  event->eventType = EVENT_INT;
		  return;
		}
	      break;

#define	RightButton	Button3
#define	MiddleButton	Button2
#define	LeftButton	Button1
	    case ButtonPress:
	      event->eventType = 0;
	      switch(((XButtonPressedEvent *)
		      &xev)->button & 0xff)
		{
		case RightButton:
		  event->eventType = EVENT_RIGHT_D;
		  return;

		case MiddleButton:
		  event->eventType = EVENT_MIDDLE_D;
		  return;

		case LeftButton:
		  event->eventType = EVENT_LEFT_D;
		  return;
		}
	      break;

	    case ButtonRelease:
	      event->eventType = 0;
	      switch(((XButtonReleasedEvent *)
		      &xev)->button&0xff)
		{
		case RightButton:
		  event->eventType = EVENT_RIGHT_U;
		  return;

		case LeftButton:
		  event->eventType = EVENT_LEFT_U;
		  return;
		}
	      break;

	    case Expose:
	      repaintWindow();
	      break;

	    case FocusIn:
	    case MapNotify:
	      mapped = TRUE;
	      iconInverted = FALSE;
	      flashIcon = FALSE;
	      repaintIcon();
	      break;

	    case FocusOut:
	    case UnmapNotify:
	      mapped = FALSE;
	      break;
	    }
	}

      if (FD_ISSET(socket, &fdmask))
	{
	  socklen_t fromLen = sizeof(event->eventSource);
	  int cc;

	  event->eventType = EVENT_NETWORK;
	  cc = recvfrom(socket, (char*)event->eventDetail,
			sizeof(MW244BPacket), 0,
		        (struct sockaddr *)&event->eventSource,
			&fromLen);
	  if (cc <= 0)
	    {
	      if (cc < 0 && errno != EINTR)
		perror("event recvfrom");
	      continue;
	    }
	  if (fromLen != sizeof(struct sockaddr_in))
	    continue;
	  ConvertIncoming(event->eventDetail);
	  return;
	}
    }
}
  void SteamScene::Update()
  {
    _timer += CherEngine::GTime.deltaTime;

    auto countLaunched = 0;
    for (int32_t i = 0; i < _spritesLaunched.size(); i++)
    {
      if (_spritesLaunched[i].launched)
      {
        countLaunched += 1;
      }
    }

    if (_timer > kDuration && countLaunched == 0)
    {
      _game.PlaySound("logoff");
      _game.StartScene("wonScene");
      return;
    }

    if (_timer >= kDuration - kOvedriveDuration && !_overdrive)
    {
      _overdrive = true;
      Overdrive();
    }
    else if (_timer + _eventDuration < kDuration - kOvedriveDuration)
    {
      _nextEventTimer -= CherEngine::GTime.deltaTime;
      if (_nextEventTimer <= 0.0f)
      {
        _nextEventTimer += _game.RandomNumber(kMinNextEvent, kMaxNextEvent);
        RandomEvent();
      }
    }

    if (_gabenEventOn || _steamsaleEventOn)
    {
      _eventTimer += CherEngine::GTime.deltaTime;

      if (_eventTimer > kEventAppearSpeed)
      {
        if (_steamsaleEventOn)
        {
          _bonusSpawn = kSteamSaleBonusSpawn;
        }

        if (_gabenEventOn)
        {
          _speedMultiplier = kGabenSpeedMultiplier;
        }
      }

      if (_eventTimer < _eventDuration - kEventBlinking)
      {
        if (_steamsaleEventOn)
        {
          _steamsale->SetAlpha(std::min(1.0f, _eventTimer / kEventAppearSpeed));
        }

        if (_gabenEventOn)
        {
          _gaben->SetAlpha(std::min(1.0f, _eventTimer / kEventAppearSpeed));
        }
      }
      else if (_eventTimer >= _eventDuration - kEventBlinking)
      {
        const auto alpha = (1.0f - std::fmod(_eventTimer - (_eventDuration - kEventBlinking), kEventBlinkingSpeed)) * 0.5f;

        if (_steamsaleEventOn)
        {
          _steamsale->SetAlpha(0.5f + alpha);
        }

        if (_gabenEventOn)
        {
          _gaben->SetAlpha(0.5f + alpha);
        }
      }

      if (_eventTimer >= _eventDuration)
      {
        if (_steamsaleEventOn)
        {
          _bonusSpawn = kDefaultBonusSpawn;
          _steamsaleEventOn = false;
          _steamsale->SetVisible(false);
        }

        if (_gabenEventOn)
        {
          _speedMultiplier = kDefaultSpeedMultiplier;
          _gabenEventOn = false;
          _gaben->SetVisible(false);
        }

        _eventTimer = 0.0f;
      }
    }

    _cursorTimer -= CherEngine::GTime.deltaTime;
    if (_cursorTimer <= 0.0f)
    {
      for (int32_t i = kCursorShadows - 1; i > 0; i--)
      {
        int32_t x = _cursorShadows[i - 1]->CenterX, y = _cursorShadows[i - 1]->CenterY;
        _cursorShadows[i]->SetCenterPosition(x, y);
      }

      int32_t x = CherEngine::GInput.MouseX, y = CherEngine::GInput.MouseY;
      CherEngine::GCamera.TransformMouse(x, y);
      _cursorShadows[0]->SetCenterPosition(x + 16, y + 16);

      _cursorTimer = 0.005f;
    }

    for (int32_t i = 0; i < _spritesLaunched.size(); i++)
    {
      auto& icon = _gameIcons[i];
      auto& spriteLaunched = _spritesLaunched[i];
      if (spriteLaunched.launched && !spriteLaunched.split)
      {
        spriteLaunched.t += _speedMultiplier * CherEngine::GTime.deltaTime;
        auto x = spriteLaunched.initialX + spriteLaunched.direction * (spriteLaunched.velocity * spriteLaunched.t * std::cos(spriteLaunched.angle));
        auto y = spriteLaunched.initialY - (spriteLaunched.velocity * spriteLaunched.t * std::sin(spriteLaunched.angle))
          + (0.5f * g * spriteLaunched.t * spriteLaunched.t);
        icon->GetMasterSprite()->SetCenterPosition(static_cast<int32_t>(x), static_cast<int32_t>(y));

        if (y > kBottomborder)
        {
          spriteLaunched.launched = false;
          icon->SetVisible(false);

          _fullSpace = std::min(_fullSpace + icon->Weight, kCapacity);
          const auto pc = _fullSpace / kCapacity;
          if (pc > 0.75f && !_spacebarRed->GetVisible())
          {
            _spacebarRed->SetVisible(true);
            _spacebarBlue->SetVisible(false);
          }


          if (_fullSpace >= kCapacity)
          {
            _game.PlaySound("logoff");
            _game.StartScene("lostScene");
            return;
          }
          else
          {
            _game.PlaySound("hardwarefail");
          }

          _spacebarBlue->SetWidthPercentage(pc);
          _spacebarRed->SetWidthPercentage(pc);

        }

        if (!spriteLaunched.split)
        {
          for (int32_t i = kCursorShadows - 1; i >= 0; i--)
          {
            // Find first shadow inside
            auto cursorShadow = _cursorShadows[i];
            if (icon->GetMasterSprite()->IsInside(cursorShadow->CenterX - 16, cursorShadow->CenterY - 16))
            {
              CherEngine::Sprite* firstLeftOutsideShadow = nullptr;
              CherEngine::Sprite* firstRightOutsideShadow = nullptr;
              for (int32_t j = i - 1; j >= 0; j--)
              {
                auto potentialShadow = _cursorShadows[j];
                if (!icon->GetMasterSprite()->IsInside(potentialShadow->CenterX - 16, potentialShadow->CenterY - 16))
                {
                  firstLeftOutsideShadow = potentialShadow;
                  break;
                }
              }

              for (int32_t j = i + 1; j < kCursorShadows; j++)
              {
                auto potentialShadow = _cursorShadows[j];
                if (!icon->GetMasterSprite()->IsInside(potentialShadow->CenterX - 16, potentialShadow->CenterY - 16))
                {
                  firstRightOutsideShadow = potentialShadow;
                  break;
                }
              }

              if (firstLeftOutsideShadow != nullptr && firstRightOutsideShadow != nullptr)
              {
                const int xDiff = std::abs(firstLeftOutsideShadow->CenterX - firstRightOutsideShadow->CenterX);
                const int yDiff = std::abs(firstLeftOutsideShadow->CenterY - firstRightOutsideShadow->CenterY);

                LD42::SplitDirection direction;

                if (xDiff == 0)
                {
                  direction = kSplitDirection_Vertical;
                }
                else if (yDiff == 0)
                {
                  direction = kSplitDirection_Horizontal;
                }
                else
                {
                  const auto angle = std::atan2(yDiff, xDiff);
                  const auto lX = firstLeftOutsideShadow->CenterX;
                  const auto rX = firstRightOutsideShadow->CenterX;
                  const auto lY = firstLeftOutsideShadow->CenterY;
                  const auto rY = firstRightOutsideShadow->CenterY;

                  if (angle < Deg2Rad * 23)
                  {
                    direction = kSplitDirection_Horizontal;
                  }
                  else if (angle < Deg2Rad * (45 + 23) && ((lX < rX  &&  lY < rY) || (lX > rX  &&  lY > rY)))
                  {
                    direction = kSplitDirection_DiagonalLR;
                  }
                  else if (angle < Deg2Rad * (45 + 23) && ((lX > rX  &&  lY < rY) || (lX < rX  &&  lY > rY)))
                  {
                    direction = kSplitDirection_DiagonalRL;
                  }
                  else
                  {
                    direction = kSplitDirection_Vertical;
                  }
                }

                spriteLaunched.split = true;
                spriteLaunched.initialMasterX = icon->GetMasterSprite()->CenterX;
                spriteLaunched.initialMasterY = icon->GetMasterSprite()->CenterY;
                spriteLaunched.t = 0.0f;
                switch (direction)
                {
                case LD42::kSplitDirection_Horizontal:
                  spriteLaunched.velocityX = 0;
                  spriteLaunched.velocityY = 20;
                  break;
                case LD42::kSplitDirection_Vertical:
                  spriteLaunched.velocityX = -30;
                  spriteLaunched.velocityY = 0;
                  break;
                case LD42::kSplitDirection_DiagonalLR:
                  spriteLaunched.velocityX = 15;
                  spriteLaunched.velocityY = 10;
                  break;
                case LD42::kSplitDirection_DiagonalRL:
                  spriteLaunched.velocityX = -15;
                  spriteLaunched.velocityY = 10;
                  break;
                default:
                  break;
                }

                _game.PlaySound("recycle");

                icon->Split(direction);
              }
              break;
            }
          }
        }
      }
      else if (spriteLaunched.launched && spriteLaunched.split)
      {
        spriteLaunched.t += _speedMultiplier * CherEngine::GTime.deltaTime;
        auto x = spriteLaunched.initialMasterX + (spriteLaunched.velocityX * spriteLaunched.t);
        auto y = spriteLaunched.initialMasterY - (spriteLaunched.velocityY * spriteLaunched.t) + (0.5f * g * spriteLaunched.t * spriteLaunched.t);
        icon->GetMasterSprite()->SetCenterPosition(static_cast<int32_t>(x), static_cast<int32_t>(y));

        auto xSecond = spriteLaunched.initialMasterX + (-spriteLaunched.velocityX * spriteLaunched.t);
        auto ySecond = spriteLaunched.initialMasterY - (-spriteLaunched.velocityY * spriteLaunched.t) + (0.5f * g * spriteLaunched.t * spriteLaunched.t);
        icon->GetSecondMasterSprite()->SetCenterPosition(static_cast<int32_t>(xSecond), static_cast<int32_t>(ySecond));

        if (y > kBottomborder && ySecond > kBottomborder)
        {
          spriteLaunched.launched = false;
          spriteLaunched.split = false;
          icon->SetVisible(false);
          icon->Restore();
        }
      }
    }

    _launchTimer -= CherEngine::GTime.deltaTime;
    if (_launchTimer <= 0.0f && _timer < kDuration)
    {
      auto launchCount = _bonusSpawn + _game.RandomPoisson();
      for (size_t i = 0; i < launchCount; i++)
      {
        LaunchRandomnIcon();
      }

      // Always launch at least one
      if (launchCount == 0)
      {
        LaunchRandomnIcon();
      }

      _launchTimer = _launchDelay;
    }
  }