//////////////////////////////////////////////
// NovaWM - Nova Window Manager for X11     //
//////////////////////////////////////////////
// By: Tim Walters                          //
//////////////////////////////////////////////
// Copyright (C) 2001-2002 Tim Walters      //
//////////////////////////////////////////////

////////////////////////////////////////////////////////////////
//This code is released under the terms of the GNU GPL. Refer //
//to the license file included with this source code.         //
////////////////////////////////////////////////////////////////


#include "novawm.h"

void
Events::NovaWM_EventLoop ()
{
  XEvent event;
  fd_set fd;
  struct timeval tv;
  time_t now;
  struct tm *lt;
  int i;

  int xfd = ConnectionNumber (display);

  while (1)
    {
      now = time (0);
      lt = gmtime (&now);
      tv.tv_usec = 0;
      tv.tv_sec = 60 - lt->tm_sec;
      FD_ZERO (&fd);
      FD_SET (xfd, &fd);
      if (select (xfd + 1, &fd, 0, 0, &tv) == 0)
	novawm.UpdateClock ();

      while (XPending (display))
	{
	  XNextEvent (display, &event);
	  switch (event.type)
	    {
	    case Expose:
	      Event_Expose (&event.xexpose);
	      break;
	    case ButtonPress:
	      Event_ButtonDown (&event.xbutton);
	      break;
	    case ButtonRelease:
	      Event_ButtonUp (&event.xbutton);
	      break;
	    case MotionNotify:
	      Event_Motion (&event.xcrossing);
	      break;
	    case MapRequest:
	      Event_Map (&event.xmaprequest);
	      break;
	    case UnmapNotify:
	      Event_Unmap (&event.xunmap);
	      break;
	    case DestroyNotify:
	      Event_Destroy (&event.xdestroywindow);
	      break;
	    case ConfigureRequest:
	      Event_Configure (&event.xconfigurerequest);
	      break;
	    case PropertyNotify:
	      Event_Property (&event.xproperty);
	      break;
	    case ColormapNotify:
	      Event_Colormap (&event.xcolormap);
	      break;
	    case ClientMessage:
	      Event_ClientMessage (&event.xclient);
	      break;
	    case FocusIn:
	      Event_FocusIn (&event.xfocus);
	      break;
	    case FocusOut:
	      Event_FocusOut (&event.xfocus);
	      break;
	    case EnterNotify:
	      Event_EnterWindow (&event.xcrossing);
	      break;
	    case LeaveNotify:
	      Event_LeaveWindow (&event.xcrossing);
	      break;
	    case KeyRelease:
	      Event_KeyUp (&event.xkey);
	    }
	}
    }

}

void
Events::Event_Expose (XExposeEvent * event)
{
#ifdef DEBUG
  //printf ("DEBUG Expose()\n");
#endif

  Novawm_Window *window = winmgr.WindowSearch (event->window);

  if (event->window == rootMenu.menuWindow ())
    {
      rootMenu.DrawItems ();
    }

  if (event->window == listMenu.menuWindow ())
    {
      listMenu.DrawItems ();
    }

  if (event->window == windowMenu.menuWindow ())
    {
      windowMenu.DrawItems ();
    }

  if ((event->window == novawm.NovaBar)
      || (event->window == novawm.nbClock)
      || (event->window == novawm.nbCurWin)
      || (event->window == novawm.nbListButton)
      || (event->window == novawm.nbButton))
    {
      //This was added for Nautilus problems but removed
      //because the bar would show up in full screen apps(like games)
      //XRaiseWindow (display, novawm.NovaBar);
      novawm.DrawBar ();
    }

  if (event->window == novawm.nbWinList)
    novawm.DrawWinList ();

  if (!window)
    return;


  winmgr.Redraw (window);
}

void
Events::Event_ButtonDown (XButtonEvent * event)
{
#ifdef DEBUG
  printf ("DEBUG ButtonDown()\n");
#endif

  if ((event->window == novawm.nbListButton) ||
      (event->window == novawm.nbButton) ||
      (event->window == novawm.nbWLstScrllUp) ||
      (event->window == novawm.nbWLstScrllDwn))
    {
      DrawDown (event->window);
      return;
    }

  Novawm_Window *window = winmgr.WindowSearch (event->window);

  if (!window)
    return;

//      window->ignoreTransient = true;

//      winmgr.SetFocus (window);

//      window->ignoreTransient = false;

  if (cfg.disableBar == true)
    {
      if (listMenu.isOpen () == true)
	listMenu.Destroy ();
      if (windowMenu.isOpen () == true)
	windowMenu.Destroy ();
      if (rootMenu.isOpen () == true)
	rootMenu.Destroy ();
    }

  //Check the titlebar buttons
  if (event->button == Button1)
    {
      if ((event->window == window->kill) ||
	  (event->window == window->stretch) ||
	  (event->window == window->hide))
	{
	  DrawDown (event->window);
	  return;
	}
    }

  XAllowEvents (display, ReplayPointer, CurrentTime);

  if (window != winmgr.focusWindow)	//Raise and give focus to the window
    {

      window->ignoreTransient = true;

      winmgr.SetFocus (window);

      window->ignoreTransient = false;
      return;
    }

  if (event->y > 18)		//Make sure it IS on the titlebar
    return;


  if (event->button == Button1 && window->titlebar == event->window)
    {
      window->ignoreTransient = true;

      winmgr.SetFocus (window);

      window->ignoreTransient = false;

      winmgr.Move (window);

    }
}

void
Events::Event_ButtonUp (XButtonEvent * event)
{
#ifdef DEBUG
  printf ("DEBUG ButtonUp()\n");
#endif

  if (downWindow)
    {
      if (event->window == novawm.nbButton)
	{
	  downWindow = None;
	  novawm.DrawBar ();
	}
      else
	{
	  DrawUp (downWindow);
	  downWindow = None;
	}
    }

  //Always close the menus after another click
  if (novaMenu.isOpen () == true)
    {
      if (event->window == novaMenu.menuWindow ())
	novawm.novaMenu_Click (novaMenu.GetSelection ());
      novaMenu.Destroy ();
    }

  if (rootMenu.isOpen () == true)
    {
      if (event->window == rootMenu.menuWindow ())
	rootMenu_Click (rootMenu.GetSelection ());
      rootMenu.Destroy ();
    }

  if (listMenu.isOpen () == true)
    {
      if (event->window == listMenu.menuWindow ())
	winmgr.listMenu_Click (listMenu.GetSelection ());
      listMenu.Destroy ();
    }

  if (windowMenu.isOpen () == true)
    {
      if (event->window == windowMenu.menuWindow ())
	windowMenu_Click (windowMenu.GetSelection ());
      windowMenu.Destroy ();
    }

  if (cfg.disableBar == true)
    {
      if (event->window == root && event->button == Button2)
	{
	  if (listMenu.isOpen () != true)
	    winmgr.Create_ListMenu (event->x_root, event->y_root);
	  else
	    {
	      winmgr.nListWindow = 0;
//                      if(winmgr.listWindow)
	      //              delete winmgr.listWindow;
	      listMenu.Destroy ();
	    }
	}

      if (event->window == root && event->button == Button3)
	{
	  if (rootMenu.isOpen () != true)
	    rootMenu.Create (event->x_root, event->y_root);
	  else
	    rootMenu.Destroy ();
	}
    }

  if (event->window == novawm.nbButton)
    if (novaMenu.isOpen () != true)
      novaMenu.Create (novawm.bar_x, NOVABAR_HEIGHT + 1);
    else
      novaMenu.Destroy ();

  if (event->window == novawm.nbWLstScrllUp)
    novawm.List_Scroll (-1);
  if (event->window == novawm.nbWLstScrllDwn)
    novawm.List_Scroll (1);

  //Get selection and close window list
  if ((novawm.lstItems >= 0)
      && (event->window != novawm.nbWLstScrllDwn) &&
      (event->window != novawm.nbWLstScrllUp) &&
      (event->window != novawm.nbWLstScrllBar))
    {
      //A item is selected, give it focus
      if ((novawm.selItem + novawm.topItem >= 0)
	  && (novawm.selItem + novawm.topItem <= novawm.lstItems)
	  && (event->window == novawm.nbWLstItemWin))
	{
	  //The selection handling here is the same with the menu.
	  winmgr.listMenu_Click (novawm.selItem + novawm.topItem);
	}

      //Close the window list
      novawm.DestroyWinList ();
    }

  if (event->window == novawm.nbListButton)
    {
      novawm.CreateWinList ();
      return;
    }

  Novawm_Window *window = winmgr.WindowSearch (event->window);

  if (!window)
    return;

  //Check the titlebar buttons
  if (event->window == window->kill)
    winmgr.Kill (window);
  if (event->window == window->stretch)
    winmgr.Stretch (window);
  if (event->window == window->hide)
    winmgr.Hide (window);

  if (event->window == window->titlebar && event->button == Button2)
    window->Shade ();

  if (event->window == window->titlebar && event->button == Button3)
    {
      if (windowMenu.isOpen () != true)
	windowMenu.Create (event->x_root, event->y_root);
      else
	windowMenu.Destroy ();
    }

}

void
Events::Event_Map (XMapRequestEvent * event)
{

#ifdef DEBUG
  printf ("DEBUG: Map()\n");
#endif

  Novawm_Window *window = winmgr.WindowSearch (event->window);

  if (!window)
    {
      winmgr.NewWindow (event->window);
      return;
    }

  winmgr.NewWindow (event->window);

  if (window->hasTitlebar == true)
    XMapWindow (display, window->titlebar);

  XMapWindow (display, window->window);

  window->SetState (NormalState);

}

void
Events::Event_Unmap (XUnmapEvent * event)
{
#ifdef DEBUG
  printf ("DEBUG Unmap()\n");
#endif

  Novawm_Window *window = winmgr.WindowSearch (event->window);

  if (winmgr.Find_UnmapIgnore (event->window) == 1)
    {
#ifdef DEBUG
      printf ("novawm: DEBUG: ignoring unmap\n");
#endif
     winmgr.RemoveUnmapIgnore (event->window);	//unmapignore--;
      return;
    }

XUnmapWindow(display,event->window);

  if (!window)
    {
#ifdef DEBUG
      printf ("DEBUG: Ignoring unmanaged window.\n");
#endif
    //  XUnmapWindow (display, event->window);
      return;
    }

#ifdef DEBUG
  printf ("DEBUG: Unmap() -- Destroying window.\n");
#endif
  window->Destroy ();
}

void
Events::Event_Motion (XCrossingEvent * event)
{
//#ifdef DEBUG
//      printf("DEBUG Motion()\n");
//#endif

  if (event->window == novawm.nbWLstItemWin)
    novawm.HighlightListItem (event->y);
  if (event->window == rootMenu.menuWindow ())
    rootMenu.HighlightItem (event->x, event->y);
  if (event->window == windowMenu.menuWindow ())
    windowMenu.HighlightItem (event->x, event->y);
  if (event->window == novaMenu.menuWindow ())
    novaMenu.HighlightItem (event->x, event->y);
  if (event->window == listMenu.menuWindow ())
    listMenu.HighlightItem (event->x, event->y);

}

void
Events::Event_Destroy (XDestroyWindowEvent * event)
{
#ifdef DEBUG
  printf ("DEBUG Destroy()\n");
#endif

  Novawm_Window *win = winmgr.WindowSearch (event->window);

  if (!win)			//Ignore destroy events of unmanaged windows
    {
      //  XDestroyWindow (display, event->window);
      return;
    }

  win->Destroy ();

}

void
Events::Event_Configure (XConfigureRequestEvent * event)
{
#ifdef DEBUG
  printf ("DEBUG Configure()\n");
#endif

  Novawm_Window *window = winmgr.WindowSearch (event->window);

  XWindowChanges winchange;
  winchange.x = event->x;
  winchange.y = event->y;
  winchange.width = event->width;
  winchange.height = event->height;
  winchange.sibling = event->above;
  winchange.stack_mode = event->detail;

  if (!window)			//Not Managed
    {
      XConfigureWindow (display, event->window, event->value_mask,
			&winchange);

      XRaiseWindow (display, event->window);
//              winmgr.NewWindow (event->window);
    }

  else				//Managed
    {
      window->x = event->x;
      window->y = event->y;
      window->orig_x = event->x;
      window->orig_y = event->y;
      window->width = event->width;
      window->height = event->height;
      window->orig_width = event->width;
      window->orig_height = event->height;

      if (window->hasTitlebar != true)	//Doesn't have a titlebar
	{
	  XMoveWindow (display, window->window, event->x, event->y);
	}

      else			//Has a titlebar
	{
	  XMoveWindow (display, window->titlebar, event->x, event->y);

	  XResizeWindow (display, window->titlebar,
			 event->width, event->height + 20);

	}

      XResizeWindow (display, window->window, event->width, event->height);

      //Move the titlebar buttons
      XMoveWindow (display, window->kill, window->width - 19, 3);
      XMoveWindow (display, window->stretch, window->width - 37, 3);
      XMoveWindow (display, window->hide, window->width - 54, 3);

      //XRaiseWindow (display, window->titlebar);
      //XRaiseWindow (display, window->window);

      window->ignoreTransient = true;

      winmgr.SetFocus (window);

      window->ignoreTransient = false;
    }
}

void
Events::Event_Property (XPropertyEvent * event)
{
  char *title;

#ifdef DEBUG
  printf ("DEBUG Property()\n");
#endif

  Novawm_Window *window = winmgr.WindowSearch (event->window);

  if (!window)
    return;


  if (event->atom == XA_WM_NAME)
    {
      XFetchName (display, window->window, &title);

      if (!title)
	window->title = "Unknown";
      else
	window->title = title;

      XClearWindow (display, window->titlebar);
      winmgr.Redraw (window);

      novawm.DrawBar ();
    }
}

void
Events::Event_Colormap (XColormapEvent * event)
{
#ifdef DEBUG
  printf ("DEBUG ColormapNotify()\n");
#endif

  Novawm_Window *window = winmgr.WindowSearch (event->window);

  if (!window)
    return;

  if (event->c_new)		//New Colormap
    {
      window->colorMap = event->c_new;
      XInstallColormap (display, event->c_new);
    }

}

void
Events::Event_ClientMessage (XClientMessageEvent * event)
{
#ifdef DEBUG
  printf ("DEBUG ClientMessage()\n");
#endif

#ifdef _WIN_GNOME_SUPPORT
  if ((event->message_type == winProtocols_Workspace) &&
      (event->window == root))
    {
      winmgr.SetDesktop (event->data.l[0]);
      return;
    }
#endif

  Novawm_Window *window = winmgr.WindowSearch (event->window);
  if (!window)
    return;

  if (event->message_type == netStateAtom)
    {
      if ((event->data.l[1] == netStateStickyAtom)
	  || (event->data.l[2] == netStateStickyAtom))
	{
	  if (event->data.l[0] == _NET_WM_STATE_ADD)
	    window->isSticky = true;
	  if (event->data.l[0] == _NET_WM_STATE_REMOVE)
	    window->isSticky = false;
	  if (event->data.l[0] == _NET_WM_STATE_TOGGLE)
	    window->isSticky = (window->isSticky) ? true : false;
	}
    }

  if (event->message_type == netDesktopAtom)
    {
      winmgr.SendToDesktop (window, event->data.l[0]);
      return;
    }

#ifdef _WIN_GNOME_SUPPORT
  if (event->message_type == winProtocols_Workspace)
    {
      winmgr.SendToDesktop (window, event->data.l[0]);
      return;
    }
#endif

  if (event->data.l[0] == IconicState)
    {
      winmgr.Hide (window);
    }

  if (event->data.l[0] == NormalState)
    {
      winmgr.SetFocus (window);
    }

}

void
Events::Event_FocusIn (XFocusChangeEvent * event)
{
#ifdef DEBUG
  printf ("DEBUG: FocusIn()\n");
#endif

  Novawm_Window *window = winmgr.WindowSearch (event->window);

  if (!window)
    return;

  //protocolAtom,SubstructureRedirectMask,takefocusAtom

  XEvent sendEvent;

  sendEvent.type = ClientMessage;
  sendEvent.xclient.window = window->window;
  sendEvent.xclient.message_type = protocolAtom;
  sendEvent.xclient.format = 32;
  sendEvent.xclient.data.l[0] = takefocusAtom;

  XSendEvent (display, window->window, false, SubstructureRedirectMask,
	      &sendEvent);

  //Grab the previous desktop key
  XGrabKey (display, XKeysymToKeycode (display, XK_bracketleft),
	    ControlMask, event->window, true, GrabModeAsync, GrabModeAsync);

  //Grab the next desktop key
  XGrabKey (display, XKeysymToKeycode (display, XK_bracketright),
	    ControlMask, event->window, true, GrabModeAsync, GrabModeAsync);

  //Set the colormap (Only added colormap support for a few programs)
  XInstallColormap (display, window->colorMap);

/*		XGrabButton(display,Button1,AnyModifier,
				window->window,
				1,ButtonPressMask,
				GrabModeSync,GrabModeAsync,
				None,None);
*/
}

void
Events::Event_FocusOut (XFocusChangeEvent * event)
{
#ifdef DEBUG
  printf ("DEBUG FocusOut\n");
#endif
  Novawm_Window *window = winmgr.WindowSearch (event->window);

  if (!window)
    return;

  if (event->mode == NotifyNormal)
    {
      winmgr.focusWindow = 0;
    }

  //Grab the previous desktop key
  XUngrabKey (display, XKeysymToKeycode (display, XK_bracketleft),
	      ControlMask, event->window);

  //Grab the next desktop key
  XUngrabKey (display, XKeysymToKeycode (display, XK_bracketright),
	      ControlMask, event->window);

  XSetInputFocus (display, root, RevertToNone, CurrentTime);

//      XUngrabButton(display,Button1,AnyModifier,
//                      window->window);

}

void
Events::Event_EnterWindow (XCrossingEvent * event)
{

  Novawm_Window *window = winmgr.WindowSearch (event->window);

  if (!window)
    return;

  if (focusType == 0)
    XSetInputFocus (display, window->window, RevertToNone, CurrentTime);

  if (event->window == window->hide)
    XSetInputFocus (display, window->hide, RevertToNone, CurrentTime);
  if (event->window == window->stretch)
    XSetInputFocus (display, window->stretch, RevertToNone, CurrentTime);
  if (event->window == window->kill)
    XSetInputFocus (display, window->kill, RevertToNone, CurrentTime);

}

void
Events::Event_LeaveWindow (XCrossingEvent * event)
{
  Novawm_Window *window = winmgr.WindowSearch (event->window);

  if (!window)
    return;

  if ((event->window == window->kill)
      || (event->window == window->stretch)
      || (event->window == window->hide))
    XSetInputFocus (display, window->window, RevertToNone, CurrentTime);

}

void
Events::Event_KeyUp (XKeyEvent * event)
{
#ifdef DEBUG
  printf ("DEBUG: KeyUp()\n");
#endif

  KeySym keysym = XKeycodeToKeysym (display, event->keycode, 0);

  switch (keysym)
    {
    case XK_bracketleft:
      winmgr.SetDesktop (winmgr.cDesktop - 1);
      break;

    case XK_bracketright:
      winmgr.SetDesktop (winmgr.cDesktop + 1);
      break;
    }

}

void
Events::rootMenu_Click (int item)
{
  if (item == 0)		//xterm
    novawm.Run ("xterm");

  if (item == 5)		//Restart
    novawm.Restart ();

  if (item == 6)		//Shutdown
    novawm.Shutdown ();
}

void
Events::windowMenu_Click (int item)
{
  if (item == 0)
    {
      winmgr.Hide (winmgr.focusWindow);
    }

  if (item == 1)
    winmgr.Stretch (winmgr.focusWindow);

  if (item == 2)
    winmgr.Resize (winmgr.focusWindow);

  if (item == 3)
    winmgr.Kill (winmgr.focusWindow);

  //Item 4 is the divider/splitter line

  if (item == 5)
    winmgr.SendToDesktop (winmgr.focusWindow, 0);

  if (item == 6)
    winmgr.SendToDesktop (winmgr.focusWindow, 1);

  if (item == 7)
    winmgr.SendToDesktop (winmgr.focusWindow, 2);

  if (item == 8)
    winmgr.SendToDesktop (winmgr.focusWindow, 3);

  if (item == 9)
    winmgr.SendToDesktop (winmgr.focusWindow, winmgr.cDesktop + 1);

  if (item == 10)
    winmgr.SendToDesktop (winmgr.focusWindow, winmgr.cDesktop - 1);
}

//The following DrawDown() and DrawUp() functions were mainly
//created button borders
//This is to simplify drawing a lowered/down border of a window
void
Events::DrawDown (Window window)
{
#ifdef DEBUG
  //printf ("DEBUG: DrawDown()\n");
#endif

  XWindowAttributes win_attrib;

  XGetWindowAttributes (display, window, &win_attrib);

  downWindow = window;

  XSetForeground (display, novawm_gc, BlackPixel (display, screen));

  XDrawLine (display, window, novawm_gc, 0, 0, win_attrib.width, 0);
  XDrawLine (display, window, novawm_gc, 0, 0, 0, win_attrib.height);

  XSetForeground (display, novawm_gc, WhitePixel (display, screen));

  XDrawLine (display, window, novawm_gc, 0, win_attrib.height - 1,
	     win_attrib.width, win_attrib.height - 1);
  XDrawLine (display, window, novawm_gc, win_attrib.width - 1, 1,
	     win_attrib.width - 1, win_attrib.height);

}

//This is to simplify drawing a raised/up border of a window
void
Events::DrawUp (Window window)
{
#ifdef DEBUG
  //printf ("DEBUG: DrawUp()\n");
#endif

  XWindowAttributes win_attrib;

  XGetWindowAttributes (display, window, &win_attrib);

  downWindow = window;

  XSetForeground (display, novawm_gc, BlackPixel (display, screen));

  XDrawLine (display, window, novawm_gc, 0, win_attrib.height - 1,
	     win_attrib.width, win_attrib.height - 1);
  XDrawLine (display, window, novawm_gc, win_attrib.width - 1, 1,
	     win_attrib.width - 1, win_attrib.height);

  XSetForeground (display, novawm_gc, WhitePixel (display, screen));

  XDrawLine (display, window, novawm_gc, 0, 0, win_attrib.width, 0);
  XDrawLine (display, window, novawm_gc, 0, 0, 0, win_attrib.height);

}


syntax highlighted by Code2HTML, v. 0.9.1