private boolean isUpdateApplicable(
     RemoteDesktopServerEvent e, RemoteDesktopServerEvent evt, float passRatio) {
   if (e.getUpdateRect() != null && evt.getUpdateRect() != null) {
     Rectangle intersect = e.getUpdateRect().intersection(evt.getUpdateRect());
     if (intersect != null && !intersect.isEmpty()) {
       float ratio =
           100
               * (intersect.width * intersect.height)
               / (e.getUpdateRect().width * e.getUpdateRect().height);
       return ratio >= passRatio;
     }
   }
   return false;
 }
  public void serverMessageReceived(RemoteDesktopServerEvent e) {
    //        if (enabled) {
    if (e.getMessageType() == RemoteDesktopServerEvent.SERVER_UPDATE_EVENT) {
      RemoteDesktopClient rfb = e.getClient();
      if (rfb.isConnected() && rfb.getDesktopWidth() > 0 && rfb.getDesktopHeight() > 0) {
        Rectangle r = e.getUpdateRect();

        int cw = rfb.getDesktopWidth();
        int ch = rfb.getDesktopHeight();
        float ratio = 100 * (r.width * r.height) / (cw * ch);
        if (ratio >= RFB_SIZE_LIMIT) {
          synchronized (rfbEvents) {
            rfbEvents.add(0, e);
            filterRfbEvents();
          }
        }
      }
    } else if (e.getMessageType() == RemoteDesktopServerEvent.SERVER_BELL_EVENT) {
      synchronized (rfbEvents) {
        rfbEvents.add(0, e);
        filterRfbEvents();
      }
    }
  }
  private RemoteDesktopServerEvent createUpdateEvent(List selectedEvents) {
    Rectangle r = null;
    RemoteDesktopServerEvent e = null;
    long time = 0;
    int p1, p2;

    for (int i = 0; selectedEvents != null && i < selectedEvents.size(); i++) {
      e = (RemoteDesktopServerEvent) selectedEvents.get(i);
      if (e.getMessageType() == RemoteDesktopServerEvent.SERVER_UPDATE_EVENT) {
        time = Math.max(time, e.getWhen());
        if (r == null) {
          r = new Rectangle(e.getUpdateRect());
        } else {
          r.x = Math.min(r.x, e.getUpdateRect().x);
          r.y = Math.min(r.y, e.getUpdateRect().y);
          p1 = r.x + r.width;
          p2 = e.getUpdateRect().x + e.getUpdateRect().width;
          if (p2 > p1) {
            r.width = p2 - r.x;
          }
          p1 = r.y + r.height;
          p2 = e.getUpdateRect().y + e.getUpdateRect().height;
          if (p2 > p1) {
            r.height = p2 - r.y;
          }
        }
      }
    }
    if (r == null) {
      return null;
    }
    //            System.out.println("Resulting rect: "+r);
    e = new RemoteDesktopServerEvent(e.getClient(), r);
    e.setWhen(time);
    return e;
  }
  public String createWaitForUpdate(
      RemoteDesktopServerEvent e, List events, UserConfiguration cfg) {

    boolean insertUpdateArea = this.insertUpdateArea;
    boolean insertUpdateExtent = this.insertUpdateExtent;
    float defaultUpdateExtent = this.defaultUpdateExtent;
    boolean insertUpdateTimeout = this.insertUpdateTimeout;
    float timeoutUpdateRatio = this.timeoutUpdateRatio;
    boolean useMinUpdateTimeout = this.useMinUpdateTimeout;
    long minUpdateTimeout = this.minUpdateTimeout;
    boolean useUpdateWait = this.useUpdateWait;
    boolean useMinUpdateWait = this.useMinUpdateWait;
    float waitUpdateRatio = this.waitUpdateRatio;
    long minUpdateWait = this.minUpdateWait;

    if (cfg != null) {
      insertUpdateArea = cfg.getBoolean("recording.waitfor.update.insertArea").booleanValue();
      insertUpdateExtent = cfg.getBoolean("recording.waitfor.update.insertExtent").booleanValue();
      defaultUpdateExtent = cfg.getInteger("recording.waitfor.update.defaultExtent").intValue();
      insertUpdateTimeout = cfg.getBoolean("recording.waitfor.update.insertTimeout").booleanValue();
      timeoutUpdateRatio = cfg.getDouble("recording.waitfor.update.timeoutRatio").floatValue();
      useMinUpdateTimeout = cfg.getBoolean("recording.waitfor.update.useMinTimeout").booleanValue();
      minUpdateTimeout = cfg.getInteger("recording.waitfor.update.minTimeout").intValue();
      useUpdateWait = cfg.getBoolean("recording.waitfor.update.useWait").booleanValue();
      useMinUpdateWait = cfg.getBoolean("recording.waitfor.update.useMinWait").booleanValue();
      waitUpdateRatio = cfg.getDouble("recording.waitfor.update.waitRatio").floatValue();
      minUpdateWait = cfg.getInteger("recording.waitfor.update.minWait").intValue();
    }

    String s = "Waitfor " + WaitforCommand.EVENT_UPDATE;
    if (insertUpdateArea) {
      s += " " + WaitforCommand.PARAM_AREA + "=" + parser.rectToString(e.getUpdateRect());
    }

    if (insertUpdateExtent) {
      float extent = defaultUpdateExtent;

      // If the 'area' param is not included, we must calculate a relative update compared to the
      // whole screen
      if (!insertUpdateArea) {
        RfbClient rfb = (RfbClient) e.getSource();
        extent =
            defaultUpdateExtent
                * ((float) (e.getUpdateRect().width * e.getUpdateRect().height)
                    / (rfb.getDesktopWidth() * rfb.getDesktopHeight()));
      }
      s += " " + WaitforCommand.PARAM_EXTENT + "=" + extent + "%";
    }

    long time = e.getWhen();
    //        System.out.println("base event, time="+e.getWhen());

    RemoteDesktopServerEvent evt;
    int count = 1;
    for (int i = 0; i < events.size(); i++) {
      evt = (RemoteDesktopServerEvent) events.get(i);
      if (!e.equals(evt)
          && isUpdateApplicable(e, evt, insertUpdateExtent ? defaultUpdateExtent : 100)) {
        count++;
        time = Math.max(time, evt.getWhen());
        //                System.out.println("applicable event #"+i+" found, time="+evt.getWhen());
      }
    }

    //        System.out.println("final time="+time);
    if (count > 1) {
      if (!useUpdateWait) {
        s += " " + WaitforCommand.PARAM_COUNT + "=" + count;
      } else {
        long wait = (long) ((e.getWhen() - lastEventListUpdateTime) * waitUpdateRatio);
        if (useMinUpdateWait) {
          wait = wait > minUpdateWait ? wait : minUpdateWait;
        }
        s += " " + WaitforCommand.PARAM_WAIT + "=" + wait;
      }
    }

    if (insertUpdateTimeout) {
      time = (long) ((time - lastEventListUpdateTime) * timeoutUpdateRatio);
      if (useMinUpdateTimeout) {
        time = time > minUpdateTimeout ? time : minUpdateTimeout;
      }
      s += " " + WaitforCommand.PARAM_TIMEOUT + "=" + time;
    }
    return s;
  }