/* toon_init.c - initialising various things
 * -copyright-
#-# 
#-# Copyright (C) 1999-2001  Robin Hogan, 2021-2026 Willem Vermin
#-# 
#-# 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#-# 
*/
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <X11/cursorfont.h>
#include <ctype.h>
#include <gtk/gtk.h>
#include <gdk/gdkx.h>
#include "toon.h"
#include "debug.h"
#include "transwindow.h"
#include "windows.h"
#include "ixpm.h"
#include "utils.h"
#include "main.h"


int toon_trans       = 0;
int ConditionalClear = 0;
int XdbeAvailable    = -42;
int UseXdbe;
int toon_monitor;

/* STARTUP FUNCTIONS */

/* Open display */
Display *ToonOpenDisplay(char *display_name)
{
   toon_display=XOpenDisplay(display_name);
   if (toon_display == NULL) {
      if (display_name == NULL && getenv("DISPLAY") == NULL)
	 strncpy(toon_error_message, _("DISPLAY environment variable not set"),
	       TOON_MESSAGE_LENGTH);
      else
	 strncpy(toon_error_message, _("Can't open display"),
	       TOON_MESSAGE_LENGTH);
      return(NULL);
   }

   //XSynchronize(toon_display,1);

   ToonInit(toon_display);
   return toon_display;
}

/* Setup graphics context and create some XRegions */
/* Currently this function always returns 0 */
int ToonInit(Display *d)
{
   static int called = 0;
   int HaveTrans     = 0;

   int screen = 0;
   XGCValues gc_values;
   XWindowAttributes attributes;
   GdkWindow *gdkwin;

   toon_display = d;
#ifdef DOUBLE_BUFFER
   static XdbeBackBuffer backbuf = 0;
   int xdbemajor;
   int xdbeminor;
   if (XdbeAvailable == -42)
      XdbeAvailable = XdbeQueryExtension(toon_display,&xdbemajor,&xdbeminor);
#else
   XdbeAvailable = 0;
#endif
   UseXdbe = XdbeAvailable && WantXdbe;

   screen = DefaultScreen(toon_display);

   *toon_message = '\0';
   if (!called)
   {
      called = 1;
      if (toon_root_override) {
	 P("toon_root_override %#lx\n",toon_root_override);
	 toon_root1 = toon_parent = toon_root_override;
	 toon_root = toon_root1;
      }
      else 
      {
	 Window Rootwindow = DefaultRootWindow(toon_display);
	 Window root;
	 int x,y;
	 unsigned int w,h,b,depth;
	 XGetGeometry(toon_display,Rootwindow,&root,
	       &x, &y, &w, &h, &b, &depth);

	 GtkWidget *gtkwin;
	 gtkwin = gtk_window_new(GTK_WINDOW_TOPLEVEL); 
	 gtk_window_set_title             (GTK_WINDOW(gtkwin),"Xpenguins-A");
	 gtk_window_set_skip_taskbar_hint (GTK_WINDOW(gtkwin),TRUE);
	 gtk_window_set_skip_pager_hint   (GTK_WINDOW(gtkwin),TRUE);
	 toon_monitor = 0;
	 // TODO let user choose which monitor to use.
	 // fullscreen on monitors differing in size gives desktop-dependent
	 // results.
	 int HaveTrans = make_trans_window(
	       toon_display,
	       gtkwin,
	       toon_monitor /*xscreen */,
	       1 /*sticky*/,
	       1 /* below*/,
	       0 /* dock */,
	       &gdkwin /* gdkwindow */,
	       &toon_root1,
	       NULL, // wantx
	       NULL  // wanty
	       );
	 P("toon_root1:");//printxwindowattrs(toon_display,toon_root1);
	 P("gdkwin:");//printgdkwindowattrs(gdkwin);

	 if(HaveTrans)
	    printf("Using a transparent click-through window for the penguins.\n");
	 else
	 {
	    printf("Cannot create a transparent click-through window.\n"
		  "Using an existing window for the penguins.\n");
	    gtkwin = NULL;
	 }
	 {
	    int x,y;
	    unsigned int w,h,b,depth;
	    XGetGeometry(toon_display,toon_root1,&root,
		  &x, &y, &w, &h, &b, &depth);
	    P("toon_root1: %#lx %d %d %d %d %d %d\n",toon_root1,x,y,w,h,b,depth);
	 }


	 if (toon_root1)
	 {
	    toon_parent = Rootwindow;
	    XSetWindowBackground(toon_display,toon_root1,0);
	    P("Background ...\n");
	    XMoveWindow(toon_display,toon_root1,0,0);
	    toon_trans = 1;
	 }
	 else
	 {
	    toon_root1 = Rootwindow;
	    // Maybe, it is LXDE: find window with name pcmanfm
	    char *DesktopSession = NULL;
	    if (getenv("DESKTOP_SESSION"))
	    {
	       DesktopSession = strdup(getenv("DESKTOP_SESSION"));
	       char *a = DesktopSession;
	       while (*a) { *a = toupper(*a); a++; }
	       if (!strncmp(DesktopSession,"LXDE",4)) 
	       {
		  Window w = Window_With_Name(toon_display, Rootwindow, "pcmanfm");
		  if(w)
		  {
		     toon_root1 = w;
		     printf("LXDE session found, using window pcmanfm\n");
		  }
	       }
	    }
	    toon_parent = Rootwindow;
	    if(DesktopSession)
	       free(DesktopSession);
	 }
	 toon_root = toon_root1;
      }
   }
   P("toon_root: %p %#lx\n",(void*)toon_display,toon_root);

   {
      Window root;
      int x,y;
      unsigned int w,h,b,depth;
      XGetGeometry(toon_display,toon_root1,&root,
	    &x, &y, &w, &h, &b, &depth);
      P("toon_root1: %#lx %d %d %d %d %d %d\n",toon_root1,x,y,w,h,b,depth);
      P("toon_root1:");//printxwindowattrs(toon_display,toon_root1);
      P("gdkwin:");//printgdkwindowattrs(gdkwin);
      toon_depth = depth;
      char *name;
      int rc = XFetchName(toon_display,toon_root1,&name);
      if (rc)
      {
	 printf("Drawing in window %#lx \"%s\" %d+%d %dx%d\n", toon_root1, name, x,y,w,h);
	 free(name);
      }
      else
	 printf("Drawing in window %#lx (no name) %d+%d %dx%d\n", toon_root1, x,y,w,h);
   }

   XGetWindowAttributes(toon_display, toon_root1, &attributes);
   toon_display_width = attributes.width;
   toon_display_height = attributes.height - toon_lift;
   if (toon_root1 != toon_parent) {
      /* Work out the position of toon_root1 with respect to toon_parent;
       * assume for now that toon_parent is the same size as the root
       * window */
      toon_x_offset = attributes.x;
      toon_y_offset = attributes.y;
   }

   /* If we want to squish the toons with the mouse then we must create
    * a window over the root window that has the same properties */
   if (toon_squish) {
      P("toon_squish\n");
      XSetWindowAttributes squish_att;
      squish_att.event_mask = ButtonPressMask;
      squish_att.override_redirect = True;
      toon_squish_window = XCreateWindow(toon_display, toon_root1, 0, 0,
	    toon_display_width, toon_display_height,
	    0, CopyFromParent, InputOnly, CopyFromParent,
	    CWOverrideRedirect | CWEventMask,
	    &squish_att);
      XDefineCursor(toon_display, toon_squish_window,
	    XCreateFontCursor(toon_display, XC_target));
      XLowerWindow(toon_display, toon_squish_window);
   }

   /* Is anyone interested in this window? If so we must inform them of
    * where the toons are by sending expose events - that way they can
    * redraw themselves when a toon walks over them */
   if (!toon_trans && (attributes.all_event_masks & ExposureMask)) 
   {
      int len = strlen(toon_message);
      toon_expose = 1;
      if (*toon_message) {
	 snprintf(toon_message + len, TOON_MESSAGE_LENGTH - len,
	       _(" and redrawing overwritten desktop icons"));
      }
      else {
	 snprintf(toon_message, TOON_MESSAGE_LENGTH,
	       _("Redrawing overwritten desktop icons"));
      }
      toon_message[TOON_MESSAGE_LENGTH-1] = '\0';
   }
   else {
      toon_expose = 0;
   }
   P("toon_expose %d\n",toon_expose);

   /* Set Graphics Context */
   gc_values.function = GXcopy;
   gc_values.graphics_exposures = False;
   gc_values.fill_style = FillTiled;
   toon_drawGC = XCreateGC(toon_display, toon_root1,
	 GCFunction | GCFillStyle | GCGraphicsExposures,
	 &gc_values);
   P("toon_drawGC %p %#lx\n",(void *)toon_display,toon_root1);

   /* Regions */
   toon_windows = XCreateRegion();

   /* Notify if the location of the client windows changes,
      or if the window we are drawing to changes size */
   if(1)
   {
      if (toon_root1 != RootWindow(toon_display, screen)) 
      {
	 if (toon_root1 == toon_parent) 
	 {
	    P("case one\n");
	    XSelectInput(toon_display, toon_root1, SubstructureNotifyMask
		  | StructureNotifyMask);
	 }
	 else 
	 {
	    P("case two %#lx\n",toon_parent);
	    XSelectInput(toon_display, toon_root1, StructureNotifyMask);
	    XSelectInput(toon_display, toon_parent, SubstructureNotifyMask);
	 }
      }
      else 
      {
	 P("case three\n");
	 XSelectInput(toon_display, toon_parent, SubstructureNotifyMask);
      }
   }
   else
      XSelectInput(toon_display,DefaultRootWindow(toon_display),SubstructureNotifyMask|StructureNotifyMask);

   toon_nwindows = 0;

   if (HaveTrans)
      XSetWindowBackground(toon_display, toon_root1, 0);

#ifdef DOUBLE_BUFFER
   BMETHOD = XdbeBackground;
   // BMETHOD = XdbeBackground;
   // BMETHOD = XdbeUndefined;
   // BMETHOD = XdbeUntouched;
   // BMETHOD = XdbeCopied;     // use this to check if dbe works; toons will not be erased

   if (UseXdbe)
   {
      if (backbuf)
	 XdbeDeallocateBackBufferName(toon_display,backbuf);
      backbuf = XdbeAllocateBackBufferName(toon_display, toon_root1, BMETHOD);
      toon_root = backbuf;
      printf("Using double buffer: %#lx window: %#lx\n",backbuf,toon_root1);
   }
#endif

   if (toon_squish_window) {
      XMapWindow(toon_display, toon_squish_window);
   }

   return 0;
}

/* Configure signal handling and the way the toons behave via a bitmask */
/* Currently always returns 0 */
int ToonConfigure(unsigned long int code)
{
   if (code & TOON_EDGEBLOCK)
      toon_edge_block=1;
   else if (code & TOON_SIDEBOTTOMBLOCK)
      toon_edge_block=2;
   else if (code & TOON_NOEDGEBLOCK)
      toon_edge_block=0;

   if (code & TOON_SOLIDPOPUPS)
      toon_solid_popups=1;
   else if (code & TOON_NOSOLIDPOPUPS)
      toon_solid_popups=0;

   if (code & TOON_SHAPEDWINDOWS)
      toon_shaped_windows=1;
   else if (code & TOON_NOSHAPEDWINDOWS)
      toon_shaped_windows=0;

   if (code & TOON_CATCHSIGNALS) {
      signal(SIGINT, ToonSignalHandler);
      signal(SIGTERM, ToonSignalHandler);
      signal(SIGHUP, ToonSignalHandler);
   }
   else if (code & TOON_NOCATCHSIGNALS) {
      signal(SIGINT, SIG_DFL);
      signal(SIGTERM, SIG_DFL);
      signal(SIGHUP, SIG_DFL);
   }

   if (code & TOON_SQUISH) {
      toon_squish = 1;
   }
   else if (code & TOON_NOSQUISH) {
      toon_squish = 0;
   }

   return 0;
}

/* Store the pixmaps to the server */
/* Returns 0 on success, otherwise the return value from the Xpm function */
int ToonInstallData(ToonData **data, int ngenera, int ntypes)
{
   int i, j, status;
   XpmAttributes attributes;
   attributes.valuemask = (
	 // XpmReturnPixels
	 // | XpmReturnExtensions | 
	 XpmExactColors | XpmCloseness | XpmDepth);
   attributes.exactColors = False;
   attributes.closeness   = 40000;
   attributes.depth       = toon_depth;
   for (i = 0; i < ngenera; ++i) {
      for (j = 0; j < ntypes; ++j) {
	 ToonData *d = data[i]+j;
	 if (d->exists && !d->master) {
	    if ((status =
		     iXpmCreatePixmapFromData(toon_display, toon_root1,  /*letop*/
			(const char**)d->image,
			&(d->pixmap), 
			&(d->mask), 
			&attributes,0))) {
	       return status;
	    }
	 }
      }
      /* Loop through the types again for any pixmaps that are copies */
      for (j = 0; j < ntypes; ++j) {
	 ToonData *d = data[i]+j;
	 if (d->exists && d->master) {
	    d->pixmap = d->master->pixmap;
	    d->mask = d->master->mask;
	 }
      }
   }

   toon_data = data;
   toon_ngenera = ngenera;
   toon_ntypes = ntypes;
   return 0;
}
