//-*-c++-*-
// ----------------------------------------------
// RCS data:
// $Date: 2003/07/09 17:26:01 $
// $Revision: 1.7 $
// $Source: /cvsroot/miwm/miwm/miwm/miwm.cc,v $
// $Id: miwm.cc,v 1.7 2003/07/09 17:26:01 sgbeal Exp $
// $RCSfile: miwm.cc,v $
// ----------------------------------------------
// Copyright by Ben Paul Wise.
// -------------------------------------------
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
// ----------------------------------------------


#include	<stdio.h>
#include	<stdlib.h>
#include	<signal.h>
#include	<errno.h>

#include	<unistd.h>
#include	<sys/types.h>
#include	<sys/wait.h>

#include	<X11/X.h>
#include	<X11/Xos.h>
#include	<X11/Xlib.h>
#include	<X11/Xutil.h>
#include	<X11/Xatom.h>

#include	"miwm.h"
#include "mipix.h"
#include "miconfig.h"
#include "version.h"

#include "miwm_framework.h"
#include "EShellProcess.h"
#include "EPathFinder.h"
// ----------------------------------------------
// these are the defining declarations of the variables
// ----------------------------------------------

// Mode mode;			// The window manager's mode. (See "miwm.h".) 
int start_x;			// The X position where the mode changed. 
int start_y;			// The Y position where the mode changed. 

Display *dpy;			// The connection to the X server. 
int display_width;		// The width of the screen we're managing. 
int display_height;		// The height of the screen we're managing. 

Window root;			// Root window of managed screen. 
Window popup;			// Third-button menu. 

GC gc;				// The default GC. 
GC gc_thin;			// The default GC but with thinner lines. 
GC menu_gc;			// The GC for the popup window (menu). 
GC size_gc;			// The GC for the popup window (menu). 

XFontStruct *font;		// Actual titlebar font. 
XFontStruct *popup_font;	// Actual menu font. 

Bool shape;			// Does server have Shape Window extension? 
int shape_event;		// ShapeEvent event type. 

// Atoms we're interested in. See the ICCCM for more information. 
Atom wm_state;
Atom wm_change_state;
Atom wm_protocols;
Atom wm_delete;
Atom wm_take_focus;
Atom wm_colormaps;

char *argv0;

WindowManager *theWM = NULL;
//-------------------------------------------------



/**
   Runs miwm::homePath()/autostart.sh, if it exists.

   Returns 0 if the file does not exist, else it returns
   the return code from fork(), which should always be 0.

   Note that using this feature currently (July 2003) leaves a defunct
   process in the process list, and i have no clue how to properly get
   rid of it. :/ (stephan)
*/
int miwm_autostart()
{
        EShellProcess sh;
        std::string thescript = miwm::homePath()+ "/autostart.sh";
        if( ! EPathFinder::isAccessible( thescript ) ) return 0;
        COUT << "forking autostart.sh..." << endl;
        int ret = sh.fork( thescript, std::cout );
        COUT << "fork() returned " << ret << endl;
        return ret;
}


extern int
main(int argc, char *argv[])  {  // (int argc, char *argv[])

   miwm::init( argc, argv );

   if( miwm::args().isHelpSet() )
   {
           miwm::show_help();
           return 0;
   }
   //COUT << "config file content: " << miwm::config() << endl;
   XGCValues gv;
   XColor exact;
   XSetWindowAttributes attr;
   struct sigaction sa;

// #define SHOWMESIZE(a) COUT << "sizeof("<<#a<<") = " << sizeof(a) << endl;
//    SHOWMESIZE(WindowManager);
//    //   SHOWMESIZE(MenuItem);

   COUT << "Starting a MIWM that was compiled at ";
   COUT << __TIME__<<", "<<__DATE__<<endl;
   COUT << MIWMVersionID << endl << flush;
   COUT << endl << flush;
  
  int memcheck = 0;  // default is for production: don't check at all.
  memcheck = miwm::args().getInt( "memcheck", memcheck );

  
  // Open a connection to the X server. 
  dpy = XOpenDisplay("");
  if (dpy == 0)
    miAbort("can't open display.");

  getResources();

  // Find the root window. 
  root = DefaultRootWindow(dpy);
  display_width = DisplayWidth(dpy, DefaultScreen(dpy));
  display_height = DisplayHeight(dpy, DefaultScreen(dpy));

  COUT << "MIWM is using memory leak check level " << memcheck << endl;

  StartMemoryLeakCheck(memcheck);

  theWM = new WindowManager();
  argv0 = argv[0];


  // Set up an error handler. 
  XSetErrorHandler(ErrorHandler);

  // Set up signal handlers. 
  signal(SIGTERM, Terminate);
  signal(SIGINT, Terminate);
  signal(SIGHUP, Terminate);

  // Ignore SIGCHLD. 
  sa.sa_handler = SIG_IGN;
#ifdef SA_NOCLDWAIT
  sa.sa_flags = SA_NOCLDWAIT;
#else
  sa.sa_flags = 0;
#endif
  sigemptyset(&sa.sa_mask);
  // this was causing complaints by some clients....
  // the client in question was FileRunner, when deleting a subdirectory
  //  sigaction(SIGCHLD, &sa, 0); (Ben)
  // Without it we get the defunct-child problem, though. :/ (stephan)
  sigaction(SIGCHLD, &sa, 0);
  // sigaction( SIGCHLD, SIG_NOWAIT ); // suggestion from a post i found in google (stephan), but i can't find NOWAIT anywhere

  // Internalize useful atoms. 
  wm_state = XInternAtom(dpy, "WM_STATE", False);
  wm_change_state = XInternAtom(dpy, "WM_CHANGE_STATE", False);
  wm_protocols = XInternAtom(dpy, "WM_PROTOCOLS", False);
  wm_delete = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
  wm_take_focus = XInternAtom(dpy, "WM_TAKE_FOCUS", False);
  wm_colormaps = XInternAtom(dpy, "WM_COLORMAP_WINDOWS", False);

  XAllocNamedColor(dpy, DefaultColormap(dpy, DefaultScreen(dpy)), miwm::config().getString("root_cursor_color", ROOT_CURSOR_COLOR ).c_str(), &theWM->rootCursorColor, &exact);
  XAllocNamedColor(dpy, DefaultColormap(dpy, DefaultScreen(dpy)), miwm::config().getString("frame_cursor_color", FRAME_CURSOR_COLOR ).c_str(), &theWM->frameCursorColor, &exact);

  XAllocNamedColor(dpy, DefaultColormap(dpy, DefaultScreen(dpy)), "Black", &theWM->parentWindowColor, &exact);

  XAllocNamedColor(dpy, DefaultColormap(dpy, DefaultScreen(dpy)), miwm::config().getString("active_border_color", ACTIVE_BORDER_COLOR ).c_str(), &theWM->activeBorderColor, &exact);
  XAllocNamedColor(dpy, DefaultColormap(dpy, DefaultScreen(dpy)), miwm::config().getString("inactive_border_color", INACTIVE_BORDER_COLOR ).c_str(), &theWM->inactiveBorderColor, &exact);

  XAllocNamedColor(dpy, DefaultColormap(dpy, DefaultScreen(dpy)), miwm::config().getString("title_color", TITLE_COLOR ).c_str(), &theWM->titleColor, &exact);
  XAllocNamedColor(dpy, DefaultColormap(dpy, DefaultScreen(dpy)), miwm::config().getString("deadly_box_color", DEADLY_BOX_COLOR ).c_str(), &theWM->boxColor, &exact);


  // Get fonts for titlebar and popup window.

  font = XLoadQueryFont(dpy, font_name);
  if (font == 0)
    font = XLoadQueryFont(dpy, "fixed");
  if (font == 0)
    miAbort("can't find a font for the titlebars.");

  popup_font = XLoadQueryFont(dpy, popup_font_name);
  if (popup_font == 0)
    popup_font = XLoadQueryFont(dpy, "fixed");
  if (popup_font == 0)
    miAbort("can't find a font for the popup window.");

  // See if the server has the Shape Window extension. 
  shape = servershapes();

  // Set up root GC: gc
  // set up frame GC: gc_frame. 
  gv.foreground = theWM->activeBorderColor.pixel;
   gv.function = GXcopy;

  gv.background = theWM->inactiveBorderColor.pixel;
  gv.font = font->fid;
  gv.line_width = 1;
  gv.subwindow_mode = IncludeInferiors;
  gc_thin = XCreateGC(dpy, root, GCForeground | GCBackground |
		      GCFunction | GCFont | GCLineWidth | GCSubwindowMode, &gv);

  // note that this line_width interacts with drawDeadlyBox.
  gv.function = GXxor;
  gv.line_width = 2;
  gc = XCreateGC(dpy, root, GCForeground | GCBackground | GCFunction |
		 GCFont | GCLineWidth | GCSubwindowMode, &gv);
  gv.function = GXcopy;
  theWM->gc_frame = XCreateGC(dpy, root, GCForeground | GCBackground | GCFunction |
			      GCFont | GCLineWidth | GCSubwindowMode, &gv);
  gv.function = GXxor;

  // Create a window for our popup.
  // this popup will be used for all kinds of things.
  popup = XCreateSimpleWindow(dpy, root, 
			      10, 10,  // x,y
			      20, 60,  // w,h
			      1,  // border width
			      theWM->activeBorderColor.pixel,  // foreground
			      theWM->inactiveBorderColor.pixel); // background
  attr.cursor = menu_cursor;
  // may need to look at moving_event_mask to get menu_event_mask correct
  theWM->menu_event_mask = ButtonMask | ButtonMotionMask | ExposureMask | 
    Button1MotionMask | Button2MotionMask | Button3MotionMask;
 
  attr.event_mask = theWM->menu_event_mask; 
  XChangeWindowAttributes(dpy, popup, CWCursor | CWEventMask  , &attr);

  if (1 == theWM->usePixmapsP) {
   attr.background_pixmap = theWM->popupBgd;
   XChangeWindowAttributes(dpy, popup,  CWBackPixmap  , &attr);
  }

  // Create menu GC.
  gv.foreground = theWM->activeBorderColor.pixel ^ theWM->inactiveBorderColor.pixel;
  gv.line_width = 1;
  gv.font = popup_font->fid;
  menu_gc = XCreateGC(dpy, popup, GCForeground | GCBackground |
		      GCFunction | GCFont | GCLineWidth | GCSubwindowMode, &gv);

  // Create size indicator GC. 
  gv.foreground = theWM->activeBorderColor.pixel;
  gv.function = GXcopy;
  size_gc = XCreateGC(dpy, popup, GCForeground | GCBackground |
		      GCFunction | GCFont | GCLineWidth | GCSubwindowMode, &gv);

  // Set up our mouse cursors. 
  initCursor();

  // Announce our interest in the root window. 
  attr.cursor = root_cursor;
  theWM->moving_event_mask =   ButtonPressMask | ButtonReleaseMask   // all up/down
    | Button1MotionMask // all mouse motion while a button1 is pressed (resize)
    | Button2MotionMask // all mouse motion while a button2 is pressed (move)
    | Button3MotionMask // all mouse motion while a button3 is pressed (move)
    | OwnerGrabButtonMask;
		
  theWM->resizing_event_mask = theWM->moving_event_mask;

  theWM->root_event_mask = SubstructureRedirectMask
    | SubstructureNotifyMask | ColormapChangeMask 
    | ButtonPressMask | PropertyChangeMask 
    | LeaveWindowMask | EnterWindowMask;
  attr.event_mask =   theWM->root_event_mask;
  XChangeWindowAttributes(dpy, root, CWCursor | CWEventMask, &attr);

  // Make sure all our communication to the server got through. 
  XSync(dpy, False);

  // See who needs looking after. 
  theWM->scanWindowTree();

  theWM->mode = wmReady;

  ::miwm_autostart();

  theWM->mainLoop();

  return 0;
}

void
WindowManager::mainLoop() {
  XEvent xev;

  for (;;) {
    XNextEvent(dpy, &xev);
    dispatch(&xev);
  }

  // you only via miAbort or Terminate,
  // so I do not bother with StopMemoryLeakCheck() here

  return;
}

void
sendConfigureNotify(Client *c)
{
  XConfigureEvent ce;

  ce.type = ConfigureNotify;
  ce.event = c->window;
  ce.window = c->window;
  ce.x = c->size.x + border;
  ce.y = c->size.y + border;
  ce.width = c->size.width - 2 * border;
  ce.height = c->size.height - 2 * border;
  ce.above = None;
  ce.border_width = c->border;
  ce.override_redirect = 0;
  XSendEvent(dpy, c->window, False, StructureNotifyMask, (XEvent *) &ce);
}

extern int
titleHeight(void)
{
  return font->ascent + font->descent + 1;
}
// ----------------------------------------------
// end of miwm.cc
// ----------------------------------------------

