procedure TFPWMWindowManager.eventButton(e: PXButtonEvent);
var
  c: TFPWMClient;
begin
  {$ifdef FPWM_TRACE}
  FPWMTrace('[TFPWMWindowManager.eventButton]');
  {$endif}
  if (e^.button = Button3) then
  begin
    circulate(e^.window = e^.root);
    Exit;
  end;
  c := windowToClient(e^.window);
  if (e^.window = e^.root) then
  begin
    if not CONFIG_TABLET_MODE then
    begin
      if (e^.button = Button1) then ShowDesktopMenu(e);
    end
    else
    begin
    end;
  end
  else
  begin
    if (c <> nil) then
    begin
      c.eventButton(e);
      Exit;
    end;
  end;
end;
procedure TFPWMWindowManager.circulate(activeFirst: Boolean);
var
  c: TFPWMClient = nil;
  i, j: Integer;
begin
    {$ifdef FPWM_TRACE}
    FPWMTrace('[TFPWMWindowManager.circulate]');
    {$endif}
    if (activeFirst) then c := m_activeClient;
    if (c = nil) then
    begin
	if (m_activeClient = nil) then i := -1
	else
        begin
            i:=0;
            while i<m_clients.count do
            begin
                  if (m_clients.items[i] = m_activeClient) then break;
                  i:= i+1;
            end;
	    if (i >= m_clients.count-1) then i := -1;
	end;
        j:=i+1;
        while (not m_clients.items[j].isNormal()) or
                (m_clients.items[j].isTransient()) do
        begin
	    if (j >= m_clients.count - 1) then j := -1;
	    if (j = i) then Exit; 
            j:= j+1;
        end;
	c := m_clients.items[j];
    end;
    c.activateAndWarp();
end;
function TFPWMWindowManager.attemptGrab(w, constrain: TWindow;
  mask, t: cint): cint;
var
  status: Integer;
begin
  {$ifdef FPWM_TRACE}
  FPWMTrace('[TFPWMWindowManager.attemptGrab]');
  {$endif}
  if (t = 0) then t := timestamp(False);
  status := XGrabPointer(display(), w, False, mask, GrabModeAsync,
	  GrabModeAsync, constrain, None, t);
  result:= status;
end;
procedure TFPWMWindowManager.releaseGrab(e: PXButtonEvent);
var
  ev: TXEvent;
begin
  {$ifdef FPWM_TRACE}
  FPWMTrace('[TFPWMWindowManager.releaseGrab]');
  {$endif}
  if (not nobuttons(e)) then
  begin
    while True do
    begin
      XMaskEvent(display(), ButtonMask or ButtonMotionMask, @ev);
      if (ev._type = MotionNotify) then continue;
      e := @ev.xbutton;
      if (nobuttons(e)) then break;
    end;
  end;
  XUngrabPointer(display(), e^.time);
  m_currentTime := e^.time;
end;
procedure TFPWMWindowManager.showDesktopMenu(e: PXButtonEvent);
begin
  {$ifdef FPWM_TRACE}
  FPWMTrace('[TFPWMWindowManager.showDesktopMenu]');
  {$endif}
  {$ifdef FPWM_USE_LCL}
  showDesktopMenuLCL(e);
  {$else}
  showDesktopMenuX11(e);
  {$endif}
end;
{$ifdef FPWM_USE_LCL}
procedure TFPWMWindowManager.showDesktopMenuLCL(e: PXButtonEvent);
var
  lMenu: TPopUpMenu;
  lExitItem: TMenuItem;
begin
  lMenu := TPopUpMenu.Create(nil);
  lExitItem := TMenuItem.Create(nil);
  lExitItem.Caption := 'Exit FPWM';
  lMenu.Items.Add(lExitItem);
  lMenu.PopUp(100, 100);
end;
{$else}
procedure TFPWMWindowManager.showDesktopMenuX11(e: PXButtonEvent);
label LoopStart;
var
  i, nh: Integer;
  allowExit: Boolean = False;
  clients: TClientList;
  n, mx, my: cint;
  width: Integer;
  maxWidth: Integer = 10;
  MENU_LABEL: string;
  selecting, prev, entryHeight, totalHeight, x, y: cint;
  warp: Boolean;
  done, drawn: Boolean;
  event: TXEvent;
  dx, dy: Integer;
  function GET_MENU_LABEL(i: Integer): string;
  begin
    if i=0 then Result := m_menuCreateLabel
    else if (allowExit and (i > clients.count)) then
      Result := 'Exit fpwm'
    else
      Result := clients.items[i-1].GetLabel();
  end;
begin
  {$ifdef FPWM_TRACE}
  FPWMTrace('[TFPWMWindowManager.showDesktopMenuX11] BEGIN');
  {$endif}
  clients := TClientList.Create;
  try
    if (e^.window = m_menuWindow) then
    begin
      {$ifdef FPWM_TRACE}
      FPWMTrace('[TFPWMWindowManager.showDesktopMenuX11] END (e^.window = m_menuWindow)');
      {$endif}
      Exit;
    end;
    i:=0;
    while i < m_hiddenClients.count do
    begin
      clients.Add(m_hiddenClients.items[i]);
      i:=i+1;
    end;
    nh := clients.count + 1;
    if (CONFIG_EVERYTHING_ON_ROOT_MENU) then
    begin
       i:=0;
       while i < m_clients.count do
       begin
             if (m_clients.items[i].isNormal()) then
		clients.add(m_clients.items[i]);
             i:=i+1;
       end;
    end;
    n := clients.count + 1;
    mx := DisplayWidth (display(), m_screenNumber) - 1;
    my := DisplayHeight(display(), m_screenNumber) - 1;
    allowExit := ((e^.x > mx-3) and (e^.y > my-3)); 
    if (allowExit) then n := n + 1;
    maxWidth := 10;
    i:=0;
    while i<n do
    begin
      MENU_LABEL := GET_MENU_LABEL(i);
      width := XTextWidth(m_menuFont, PChar(MENU_LABEL), Length(MENU_LABEL));
      if (width > maxWidth) then maxWidth := width;
      Inc(i);
    end;
    maxWidth := maxWidth + 32;
    selecting := -1;
    prev := -1;
    entryHeight := m_menuFont^.ascent + m_menuFont^.descent + 4;
    totalHeight := entryHeight * n + 13;
    x := e^.x - maxWidth div 2;
    y := e^.y - 2;
    warp := False;
    if (x < 0) then
    begin
	e^.x := e^.x - x;
        x := 0;
        warp := True;
    end
    else if (x + maxWidth >= mx) then
    begin
	e^.x := e^.x - (x + maxWidth - mx);
        x := mx - maxWidth;
        warp := True;
    end;
    if (y < 0) then
    begin
	e^.y := e^.y - y;
        y := 0;
        warp := True;
    end
    else if (y + totalHeight >= my) then
    begin
      e^.y := e^.y - (y + totalHeight - my);
      y := my - totalHeight;
      warp := True;
    end;
    if (warp) then
       XWarpPointer(display(), None, root(),
	   None, None, None, None, e^.x, e^.y);
    XMoveResizeWindow(display(), m_menuWindow, x, y, maxWidth, totalHeight);
    XSelectInput(display(), m_menuWindow, MenuMask);
    XMapRaised(display(), m_menuWindow);
    if (attemptGrab(m_menuWindow, None, MenuGrabMask, e^.time) <> GrabSuccess) then
    begin
	XUnmapWindow(display(), m_menuWindow);
        {$ifdef FPWM_TRACE}
        FPWMTrace('[TFPWMWindowManager.showDesktopMenuX11] END (attemptGrab <> GrabSuccess)');
        {$endif}
	Exit;
    end;
    done := False;
    drawn := False;
    while (not done) do
    begin
LoopStart:
        XMaskEvent(display(), MenuMask, @event);
	case (event._type) of
	ButtonPress:
        begin
        end;
	ButtonRelease:
        begin
	    if (drawn) then
            begin
		if (event.xbutton.button <> e^.button) then goto LoopStart;
		x := event.xbutton.x;
		y := event.xbutton.y - 11;
		i := y div entryHeight;
		if (selecting >= 0) and (y >= selecting * entryHeight - 3) and
		   (y <= (selecting+1) * entryHeight - 3) then i := selecting;
		if (x < 0) or (x > maxWidth) or (y < -3) then i := -1
		else if (i < 0) or (i >= n) then i := -1;
	    end
            else
            begin
		selecting := -1;
	    end;
	    if (not nobuttons(@event.xbutton)) then i := -1;
	    releaseGrab(@event.xbutton);
	    XUnmapWindow(display(), m_menuWindow);
	    selecting := i;
	    done := True;
	end;
	MotionNotify:
	begin
            if (not drawn) then goto LoopStart;
	    x := event.xbutton.x;
	    y := event.xbutton.y - 11;
	    prev := selecting;
	    selecting := y div entryHeight;
	    if (prev >= 0) and (y >= prev * entryHeight - 3) and
		(y <= (prev+1) * entryHeight - 3) then selecting := prev;
	    if (x < 0) or (x > maxWidth) or (y < -3) then selecting := -1
	    else if (selecting < 0) or (selecting > n) then selecting := -1;
	    if (selecting = prev) then goto LoopStart;
	    if (prev >= 0) and (prev < n) then
            begin
		XFillRectangle(display(), m_menuWindow, m_menuGC,
			       4, prev * entryHeight + 9,
			       maxWidth - 8, entryHeight);
	    end;
	    if (selecting >= 0) and (selecting < n) then
            begin
		XFillRectangle(display(), m_menuWindow, m_menuGC,
			       4, selecting * entryHeight + 9,
			       maxWidth - 8, entryHeight);
	    end;
	end;
	Expose:
	begin
            XClearWindow(display(), m_menuWindow);
	    XDrawRectangle(display(), m_menuWindow, m_menuGC, 2, 7,
	      maxWidth - 5, totalHeight - 10);
            i:=0;
            while i<n do
            begin
                MENU_LABEL := GET_MENU_LABEL(i);
                dx := XTextWidth(m_menuFont, PChar(MENU_LABEL),
				    Length(MENU_LABEL));
		dy := i * entryHeight + m_menuFont^.ascent + 10;
		if (i >= nh) then
                begin
                   MENU_LABEL := GET_MENU_LABEL(i);
		    XDrawString(display(), m_menuWindow, m_menuGC,
			maxWidth - 8 - dx, dy,
			PChar(MENU_LABEL), Length(MENU_LABEL));
		end
                else
                begin
                    MENU_LABEL := GET_MENU_LABEL(i);
		    XDrawString(display(), m_menuWindow, m_menuGC, 8,
			dy, PChar(MENU_LABEL), Length(MENU_LABEL));
		end;
                i:= i+1;
            end;
	    if (selecting >= 0) and (selecting < n) then begin
		XFillRectangle(display(), m_menuWindow, m_menuGC,
		       4, selecting * entryHeight + 9,
		       maxWidth - 8, entryHeight);
	    end;
	    drawn := True;
	end;
        else
          WriteLn(Format('fpwwm: unknown event type %d', [event._type]));
        end;
    end;
    if (selecting = n-1) and (allowExit) then
    begin
	m_signalled := -1;
        {$ifdef FPWM_TRACE}
        FPWMTrace('[TFPWMWindowManager.showDesktopMenuX11] END (selecting = n-1) and (allowExit)');
        {$endif}
	Exit;
    end;
    if (selecting >= 0) then
    begin
	if (selecting = 0) then
        begin
	    spawn();
	end
        else if (selecting < nh) then
        begin
	    clients.items[selecting - 1].unhide(True);
	end else if (selecting < n) then
        begin
	    if (CONFIG_CLICK_TO_FOCUS) then begin
		clients.items[selecting - 1].activate();
	    end
            else begin
		clients.items[selecting - 1].mapRaised();
	    end;
	    clients.items[selecting - 1].ensureVisible();
	end;
    end;
  finally
    clients.Free;
  end;
  {$ifdef FPWM_TRACE}
  FPWMTrace('[TFPWMWindowManager.showDesktopMenuX11] END');
  {$endif}
end;
{$endif}
procedure TFPWMWindowManager.showGeometry(x, y: cint);
var
  str: array[0..19] of Char;
  width, height, mx, my: cint;
begin
  {$ifdef FPWM_TRACE}
  FPWMTrace('[TFPWMWindowManager.showGeometry]');
  {$endif}
  str := Format('%d %d' + LineEnding, [x, y]);
  width := XTextWidth(m_menuFont, str, Length(str)) + 8;
  height := m_menuFont^.ascent + m_menuFont^.descent + 8;
  mx := DisplayWidth (display(), m_screenNumber) - 1;
  my := DisplayHeight(display(), m_screenNumber) - 1;
  XMoveResizeWindow(display(), m_menuWindow,
      (mx - width) div 2, (my - height) div 2, width, height);
  XClearWindow(display(), m_menuWindow);
  XMapRaised(display(), m_menuWindow);
  XDrawString(display(), m_menuWindow, m_menuGC, 4, 4 + m_menuFont^.ascent,
	str, Length(str));
end;
procedure TFPWMWindowManager.removeGeometry();
begin
  {$ifdef FPWM_TRACE}
  FPWMTrace('[TFPWMWindowManager.removeGeometry]');
  {$endif}
  XUnmapWindow(display(), m_menuWindow);
end;