;;; xwem-struct.el --- Core XWEM structures.

;; Copyright (C) 2004,2005 by XWEM Org.

;; Author: Zajcev Evgeny <zevlg@yandex.ru>
;; Created: Tue Aug 24 12:43:45 MSD 2004
;; Keywords: xwem
;; X-CVS: $Id: xwem-struct.el,v 1.5 2005/04/04 19:54:16 lg Exp $

;; This file is part of XWEM.

;; XWEM 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, or (at your option)
;; any later version.

;; XWEM 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 XEmacs; see the file COPYING.  If not, write to the Free
;; Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
;; 02111-1307, USA.

;;; Synched up with: Not in FSF

;;; Commentary:

;; 

;;; Code:

(require 'xlib-xc)

(eval-when-compile
  ;; Shut up compiler
  (defvar xwem-current-cl)
  (defvar xwem-last-cl)
  (defvar xwem-frames-list)
  (defvar xwem-clients)
  (defvar xwem-client-ev-mask))

(define-error 'xwem-error "XWEM error")


;;;; Root window
(defvar xwem-root-display nil
  "Default X Display for XWEM.
Use `xwem-dpy' to get it.")
(defvar xwem-root-window nil
  "Default root window of `xwem-dpy'.")
(defvar xwem-root-geometry nil
  "Geometry of `xwem-rootwin'.")

;;;###xwem-autoload
(defmacro xwem-dpy ()
  "Return default X display for XWEM."
  'xwem-root-display)
;;;###xwem-autoload
(defmacro xwem-rootwin ()
  "Return default root window of `xwem-dpy'."
  'xwem-root-window)
;;;###xwem-autoload
(defmacro xwem-rootgeom ()
  'xwem-root-geometry)

(defsetf xwem-dpy () (xdpy)
  "Set default X display for XWEM."
  `(progn
     (setq xwem-root-display ,xdpy)
     (unless (X-Dpy-p (xwem-dpy))
       (error 'xwem-error "Can't open display"))
     (setq xwem-root-window (XDefaultRootWindow (xwem-dpy)))
     (setq xwem-root-geometry (XGetGeometry (xwem-dpy) (xwem-rootwin)))))

;;;; Frame structures
(defstruct xwem-frame
  type                                  ; frame type (desktop, embedded, dedicated, embedded-desktop, etc)
  xwin                                  ; X window 
  xgeom                                 ; frame geometry
  (name "default")                      ; frame name
  rootwin                               ; XWEM's root window for frame
  selwin                                ; XWEM's selected window
  link-next                             ; Link to next xwem's frame in linkage
  link-prev                             ; Link to prev xwem's frame in linkage
  state                                 ; 'mapped, 'unmapped, 'destroyed

  plist)                                ; User defined plist

(defstruct xwem-frame-saved
  frame                                 ; nil or `xwem-frame'
  selected-p                            ; non-nil if frame was selected
  type
  name
  xgeom
  state                                 ; 
  plist                                 ; as in xwem-frame
  winconfig)

(defsubst xwem-frame-get-prop (frame prop)
  (plist-get (xwem-frame-plist frame) prop))

(defsubst xwem-frame-rem-prop (frame prop)
  "From FRAME's plist remove property PROP."
  (setf (xwem-frame-plist frame)
        (plist-remprop (xwem-frame-plist frame) prop)))

(defsubst xwem-frame-put-prop (frame prop val)
  "Put PROP with VAL to FRAME's properties list."
  (if prop
      (setf (xwem-frame-plist frame)
            (plist-put (xwem-frame-plist frame) prop val))
    (xwem-frame-rem-prop frame prop)))
(put 'xwem-frame-put-prop 'lisp-indent-function 2)

(defvar xwem-current-frame nil
  "Currently selected frame.
Do not access/modify this variable directly, use `xwem-frame-selected'.")

(defmacro xwem-frame-selected ()
  "Return selected frame."
  'xwem-current-frame)
(defsetf xwem-frame-selected () (frame)
  `(setq xwem-current-frame ,frame))

(defsubst xwem-frame-selected-p (frame)
  "Return non-nil if FRAME is selected."
  (eq frame (xwem-frame-selected)))

(defsubst xwem-frame-desktop-p (frame)
  "Return non-nil if FRAME is desktop."
  (memq (xwem-frame-type frame)
        '(desktop embedded-desktop)))

(defsubst xwem-frame-embedded-p (frame)
  "Return non-nil if FRAME is embedded frame."
  (memq (xwem-frame-type frame)
        '(embedded embedded-desktop)))

(defsubst xwem-frame-dedicated-p (frame)
  "Return non-nil if FRAME is dedicated frame."
  (memq (xwem-frame-type frame)
        '(dedicated)))

(defmacro xwem-frame-x (frame)
  `(X-Geom-x (xwem-frame-xgeom ,frame)))
(defsetf xwem-frame-x (frame) (x)
  `(setf (X-Geom-x (xwem-frame-xgeom ,frame)) ,x))

(defmacro xwem-frame-y (frame)
  `(X-Geom-y (xwem-frame-xgeom ,frame)))
(defsetf xwem-frame-y (frame) (y)
  `(setf (X-Geom-y (xwem-frame-xgeom ,frame)) ,y))

(defmacro xwem-frame-width (frame)
  `(X-Geom-width (xwem-frame-xgeom ,frame)))
(defsetf xwem-frame-width (frame) (width)
  `(setf (X-Geom-width (xwem-frame-xgeom ,frame)) ,width))

(defmacro xwem-frame-height (frame)
  `(X-Geom-height (xwem-frame-xgeom ,frame)))
(defsetf xwem-frame-height (frame) (height)
  `(setf (X-Geom-height (xwem-frame-xgeom ,frame)) ,height))

(defmacro xwem-frame-border-width (frame)
  `(X-Geom-border-width (xwem-frame-xgeom ,frame)))
(defsetf xwem-frame-border-width (frame) (height)
  `(setf (X-Geom-border-width (xwem-frame-xgeom ,frame)) ,height))

(defmacro xwem-frame-title-height (frame)
  "Return FRAME's title height."
  `(xwem-frame-property ,frame 'title-height))
(defsetf xwem-frame-title-height (frame) (new-title-height)
  "Set FRAME's title height to NEW-TITLE-HEIGHT."
  `(xwem-frame-set-property ,frame 'title-height ,new-title-height))

(defmacro xwem-frame-inner-border-width (frame)
  "Return FRAME's inner border width."
  `(xwem-frame-property ,frame 'inner-border-width))
(defsetf xwem-frame-inner-border-width (frame) (new-inner-border-width)
  "Set FRAME's inner border width to be NEW-INNER-BORDER-WIDTH."
  `(xwem-frame-set-property ,frame 'inner-border-width ,new-inner-border-width))

(defsubst xwem-frame-alive-p (frame)
  "Return non-nil if FRAME is alive XWEM frame."
  (and (xwem-frame-p frame)
       (memq frame xwem-frames-list)
       (not (eq (xwem-frame-type frame) 'destroyed))))

(defsubst xwem-frame-mapped-p (frame)
  "Return non-nil if xwem FRAME is mapped."
  (and (xwem-frame-p frame)
       (eq (xwem-frame-state frame) 'mapped)))

(defsubst xwem-frame-cl (frame)
  "Return currently active xwem client in FRAME."
  (xwem-win-cl (xwem-frame-selwin frame)))


;;;; Win structures
(defstruct xwem-win
  id                                    ; unique window id
  geom                                  ; window geometry (border width is internal window width)
  clients                               ; xwem clients list managed in window
  cl                                    ; Current window's client
  frame                                 ; xwem frame
  dead                                  ; non-nil if window is dead
  deleted                               ; non-nil if window was deleted
  next                                  ; next window in windows chain
  prev                                  ; previous window in windows chain
  hchild                                ; horisontal child (if any)
  vchild                                ; vertical child (if any)
  parent                                ; parent window
  
  plist)                                ; User defined plist

(defstruct (xwem-win-saved (:predicate xwem-iswinsaved-p))
  id                                    ; saved window id
  geom                                  ; saved window geometry
  clients                               ; clients managed in window
  cl                                    ; Current window's client
  plist                                 ; properties
  selwin-p                              ; non-nil if window is selected in frame
  first-hchild first-vchild
  next prev)

(defstruct (xwem-win-config (:predicate xwem-iswinconfig-p))
  frame                                 ; window's frame
  frame-xgeom                           ; saved frame X-Geom
  frame-properties                      ; saved frame properties
  current-cl                            ; cl in selected window
  min-width min-height
  saved-root-window)

(defsubst xwem-win-alive-p (window)
  "Return non-nil if WINDOW is alive."
  (and (xwem-win-p window)
       (xwem-frame-alive-p (xwem-win-frame window))
       (not (xwem-win-deleted window))
       (not (xwem-win-dead window))))

(defmacro xwem-win-x (win)
  `(X-Geom-x (xwem-win-geom ,win)))
(defsetf xwem-win-x (win) (x)
  `(setf (X-Geom-x (xwem-win-geom ,win)) ,x))

(defmacro xwem-win-y (win)
  `(X-Geom-y (xwem-win-geom ,win)))
(defsetf xwem-win-y (win) (y)
  `(setf (X-Geom-y (xwem-win-geom ,win)) ,y))

(defmacro xwem-win-width (win)
  `(X-Geom-width (xwem-win-geom ,win)))
(defsetf xwem-win-width (win) (width)
  `(setf (X-Geom-width (xwem-win-geom ,win)) ,width))

(defmacro xwem-win-height (win)
  `(X-Geom-height (xwem-win-geom ,win)))
(defsetf xwem-win-height (win) (height)
  `(setf (X-Geom-height (xwem-win-geom ,win)) ,height))

(defmacro xwem-win-border-width (win)
  `(X-Geom-border-width (xwem-win-geom ,win)))
(defsetf xwem-win-border-width (win) (border-width)
  `(setf (X-Geom-border-width (xwem-win-geom ,win)) ,border-width))

(defsubst xwem-win-get-prop (win prop)
  "Get WIN's property PROP."
  (plist-get (xwem-win-plist win) prop))

(defsubst xwem-win-rem-prop (win prop)
  "Remove WIN's property PROP."
  (setf (xwem-win-plist win)
        (plist-remprop (xwem-win-plist win) prop)))

(defsubst xwem-win-put-prop (win prop val)
  "Set WIN's property PROP to VAL."
  (if val
      (setf (xwem-win-plist win)
            (plist-put (xwem-win-plist win) prop val))
    (xwem-win-rem-prop win prop)))
(put 'xwem-win-put-prop 'lisp-indent-function 2)

(defmacro xwem-win-selected ()
  "Return selected window."
  '(and (xwem-frame-alive-p (xwem-frame-selected))
        (xwem-frame-selwin (xwem-frame-selected))))

(defmacro xwem-win-selected-p (win)
  "Return non-nil if WIN is currently selected window."
  `(eq ,win (xwem-win-selected)))

(defsubst xwem-win-selwin-p (win)
  "Return non-nil if WIN is localy selected window in WIN's frame."
  (and (xwem-win-p win)
       (eq win (xwem-frame-selwin (xwem-win-frame win)))))

(defsubst xwem-win-cl-current-p (cl &optional win)
  "Return non-nil if CL is current WIN's client."
  (unless win
    (setq win (xwem-cl-win cl)))
  (when (xwem-win-p win)
    (eq cl (xwem-win-cl win))))


;;;; Client structures
(defstruct xwem-hints
  ;; TODO: add more
  wm-normal-hints
  wm-hints
  wm-class
  wm-command
  wm-name
  wm-icon-name
  wm-transient-for
  wm-protocols)

(defstruct xwem-cl
  xwin                                  ; CL's X window
  (ev-mask 0)                           ; CL's event maks
  initial-xattrs                        ; X-Attr when CL just initialized
  initial-xgeom                         ; X-Geom when CL just initialized

  xgeom                                 ; Current CL's X-Geom
  new-xgeom                             ; Wishable CL's X-Geom (for refiting)

  hints                                 ; xwem-hints
  transient-for                         ; non-nil if client is transient for window

  manage-spec                           ; MANAGE-SPEC which was used to manage client.
  win                                   ; xwem-win now (only for windowing manage types)
  translist                             ; list of transient-for windows for this client

  (state 'unknown)                      ; state of client, 'active, 'inactive, 'iconified, 'unknown, etc
  start-time                            ; start-time
  recency                               ; last time when CL was active

  local-variables                       ; client local variables list
  sys-plist                             ; system plist
  plist                                 ; user defined plist
  )

(defmacro xwem-cl-destroyed-p (cl)
  "Return non-nil if CL has already destroyed xwin."
  `(eq (xwem-cl-state ,cl) 'destroyed))

(defmacro xwem-cl-win-geom (cl)
  "Get geometry for client CL. Actually return xwem window geometry."
  `(xwem-win-geom (xwem-cl-win ,cl)))

;; User plist
(defsubst xwem-cl-get-prop (cl prop)
  "From CL's property list get property PROP."
  (plist-get (xwem-cl-plist cl) prop))

(defsubst xwem-cl-rem-prop (cl prop)
  "From CL's property list remove property PROP."
  (setf (xwem-cl-plist cl) (plist-remprop (xwem-cl-plist cl) prop)))

(defsubst xwem-cl-put-prop (cl prop val)
  "In CL's property list put property PROP with value VAL.
If VAL is nil - remove property."
  (if val
      (setf (xwem-cl-plist cl) (plist-put (xwem-cl-plist cl) prop val))
    (xwem-cl-rem-prop cl prop)))
(put 'xwem-cl-put-prop 'lisp-indent-function 2)

;; System plist
(defsubst xwem-cl-get-sys-prop (cl prop)
  "From CL's system property list get property PROP."
  (plist-get (xwem-cl-sys-plist cl) prop))

(defsubst xwem-cl-rem-sys-prop (cl prop)
  "From CL's system property list remove property PROP."
  (setf (xwem-cl-sys-plist cl) (plist-remprop (xwem-cl-sys-plist cl) prop)))

(defsubst xwem-cl-put-sys-prop (cl prop val)
  "In CL's system property list put property PROP with value VAL.
If VAL is nil - remove property."
  (if val
      (setf (xwem-cl-sys-plist cl) (plist-put (xwem-cl-sys-plist cl) prop val))
    (xwem-cl-rem-sys-prop cl val)))
(put 'xwem-cl-put-sys-prop 'lisp-indent-function 2)

(defmacro xwem-cl-manage-type (cl)
  "Return CL's manage type name."
  `(car (xwem-cl-manage-spec ,cl)))
(defsetf xwem-cl-manage-type (cl) (new-type)
  `(setf (xwem-cl-manage-spec ,cl) (list ,new-type)))
  
(defmacro xwem-cl-selected ()
  "Return currently selected Client.
May be nil if no current client."
  'xwem-current-cl)
(defsetf xwem-cl-selected () (cl)
  `(setq xwem-current-cl ,cl))

(defmacro xwem-last-client ()
  "Return last selected client."
  'xwem-last-cl)
(defsetf xwem-last-client () (cl)
  `(setq xwem-last-cl ,cl))

(defsubst xwem-cl-selected-p (cl)
  "Return non-nil if CL is selected client.
If CL is not valid `xwem-cl' structure, nill will be returned."
  (and (xwem-cl-p cl) (eq cl (xwem-cl-selected))))

(defsubst xwem-cl-frame (cl)
  "Return frame where CL."
  (let ((win (xwem-cl-win cl)))
    (and (xwem-win-p win) (xwem-win-frame win))))

(defsubst xwem-cl-alive-p (cl)
  "Return non-nil if CL is alive i.e. not in 'destroyed state."
  (and (xwem-cl-p cl) (not (eq (xwem-cl-state cl) 'destroyed))))

(defsubst xwem-cl-managed-p (cl &optional states)
  "Return non-nil if CL ins't in withdrawn state."
  (and (xwem-cl-p cl)
       (memq (xwem-cl-state cl) (or states '(active inactive iconified)))))

(defsubst xwem-cl-active-p (cl)
  "Return non-nil if CL is in active state."
  (eq (xwem-cl-state cl) 'active))

;; wm accessors
(defsubst xwem-cl-wm-name (cl)
  "Return cl's WM_NAME."
  (xwem-hints-wm-name (xwem-cl-hints cl)))
(defsetf xwem-cl-wm-name (cl) (name)
  "Set CL's WM_NAME to NAME."
  `(setf (xwem-hints-wm-name (xwem-cl-hints ,cl)) ,name))

(defsubst xwem-cl-wm-icon-name (cl)
  "Return cl's WM_ICON_NAME."
  (xwem-hints-wm-icon-name (xwem-cl-hints cl)))
(defsetf xwem-cl-wm-icon-name (cl) (icon-name)
  "Set CL's WM_ICON_NAME to ICON-NAME."
  `(setf (xwem-hints-wm-icon-name (xwem-cl-hints ,cl)) ,icon-name))

(defsubst xwem-cl-wm-hints (cl)
  "Return cl's WM_HINTS."
  (xwem-hints-wm-hints (xwem-cl-hints cl)))
(defsetf xwem-cl-wm-hints (cl) (hints)
  "Set CL's WM_HINTS to HINTS."
  `(setf (xwem-hints-wm-hints (xwem-cl-hints ,cl)) ,hints))

(defsubst xwem-cl-wm-normal-hints (cl)
  "Return cl's WM_NORMAL_HINTS."
  (xwem-hints-wm-normal-hints (xwem-cl-hints cl)))
(defsetf xwem-cl-wm-normal-hints (cl) (wnh)
  "Set CL's WM_NORMAL_HINTS to WNH."
  `(setf (xwem-hints-wm-normal-hints (xwem-cl-hints ,cl)) ,wnh))

(defsubst xwem-cl-wm-class (cl)
  "Return cl's WM_CLASS."
  (xwem-hints-wm-class (xwem-cl-hints cl)))
(defsetf xwem-cl-wm-class (cl) (class)
  "Set CL's WM_CLASS to CLASS."
  `(setf (xwem-hints-wm-class (xwem-cl-hints ,cl)) ,class))

(defsubst xwem-cl-wm-command (cl)
  "Return cl's WM_COMMAND."
  (xwem-hints-wm-command (xwem-cl-hints cl)))
(defsetf xwem-cl-wm-command (cl) (command)
  "Set CL's WM_COMMAND to COMMAND."
  `(setf (xwem-hints-wm-command (xwem-cl-hints ,cl)) ,command))

(defsubst xwem-cl-wm-transient-for (cl)
  "Return cl's WM_TRANSIENT_FOR."
  (xwem-hints-wm-command (xwem-cl-hints cl)))
(defsetf xwem-cl-wm-transient-for (cl) (wtf)
  "Set CL's WM_TRANSIENT_FOR to WTF."
  `(setf (xwem-hints-wm-transient-for (xwem-cl-hints ,cl)) ,wtf))

(defsubst xwem-cl-wm-protocols (cl)
  "Return cl's WM_PROTOCOLS."
  (xwem-hints-wm-command (xwem-cl-hints cl)))
(defsetf xwem-cl-wm-protocols (cl) (protocols)
  "Set CL's WM_PROTOCOLS to PROTOCOLS."
  `(setf (xwem-hints-wm-protocols (xwem-cl-hints ,cl)) ,protocols))


;;;; Minibuffer
(defstruct xwem-minib
  frame                                 ; Emacs frame
  cl                                    ; Corresponding xwem client

  xgeom                                 ; parent geometry
  xwin                                  ; parent xwindow

  plist                                 ; User defined plist
  )

(defmacro xwem-minib-get-prop (m prop)
  `(plist-get (xwem-minib-plist ,m) ,prop))

(defmacro xwem-minib-put-prop (m prop val)
  `(setf (xwem-minib-plist ,m)
         (plist-put (xwem-minib-plist ,m) ,prop ,val)))
(put 'xwem-minib-put-prop 'lisp-indent-function 2)

(defmacro xwem-minib-rem-prop (m prop)
  `(setf (xwem-minib-plist ,m)
         (plist-remprop (xwem-minib-plist ,m) ,prop)))

(defmacro xwem-minib-cl-xgeom (m)
  "Return client's X geometry of minibuffer M."
  `(xwem-cl-xgeom (xwem-minib-cl ,m)))
(defsetf xwem-minib-cl-xgeom (m) (xgeom)
  `(setf (xwem-cl-xgeom (xwem-minib-cl ,m)) ,xgeom))

(defmacro xwem-minib-cl-xwin (m)
  "Return clien's X window of minibuffe M."
  `(xwem-cl-xwin (xwem-minib-cl ,m)))
(defsetf xwem-minib-cl-xwin (m) (xwin)
  `(setf (xwem-cl-xwin (xwem-minib-cl ,m)) ,xwin))


;;; Various macros

;; Defining deffered funcalls
;;; Deffering related stuff
(require 'dll)

(defvar xwem-pre-deffering-hook nil
  "*Hooks to run before deffering.")
(defvar xwem-post-deffering-hook nil
  "*Hooks to run after deffering complete.
`xwem-post-deffering-hook' clears every time it runs.")

(defvar xwem-deffered-dll (dll-create)
  "Double linked list of deffered things.")

(defvar xwem-deffering-p nil
  "Non-nil mean we are running deffered function.")

(defun xwem-deffered-push (fun &rest args)
  (let*  ((dummy (dll-get-dummy-node xwem-deffered-dll))
          (node  (elib-node-right dummy))
          (exists nil))
    (while (and (not (eq node dummy))
                (not (and (funcall
                           #'(lambda (e1 e2)
                               (and (eq (car e1) (car e2))
                                    (if (and (listp (cdr e1)) (listp (cdr e2))
                                             (= (length (cdr e1))
                                                (length (cdr e2))))
                                        (not (memq nil (mapcar*
                                                        'eq (cdr e1) (cdr e2))))
                                      (eq (cdr e1) (cdr e2)))))
                           (cons fun args)
                           (dll-element xwem-deffered-dll node))
                          (setq exists t))))
      (setq node (elib-node-right node)))

    (if exists
        (dll-delete xwem-deffered-dll node)
      (enqueue-eval-event 'xwem-deffered-process nil))

    (xwem-debug 'xwem-deffered "---------> IN %S" 'fun)
    (dll-enter-last xwem-deffered-dll (cons fun args))))

(defun xwem-deffered-process (obj-notused)
  "Process deffering commands."
  (declare (special xwem-deffering-p))

  (unless xwem-deffering-p
    (let ((xwem-deffering-p t))
      (run-hooks 'xwem-pre-deffering-hook)
      (setq xwem-pre-deffering-hook nil)))

  (while (not (dll-empty xwem-deffered-dll))
    (let ((el (dll-first xwem-deffered-dll))
          (xwem-deffering-p t))
      (xwem-debug 'xwem-deffered "<--------- OUT %S" '(car el))
      (dll-delete-first xwem-deffered-dll)
      (apply (car el) (cdr el))))

  (unless xwem-deffering-p
    (let ((xwem-deffering-p t))
      (run-hooks 'xwem-post-deffering-hook)
      (setq xwem-post-deffering-hook nil))))

(defun xwem-add-hook-post-deffering (hook &optional append)
  "Add HOOK to `xwem-post-deffering-hook'."
  (add-hook 'xwem-post-deffering-hook hook append)
  ;; Run it to be sure to enter deffering
  (xwem-deffered-push 'ignore))

;; Dont know where to put this macro, so putten here.
(defmacro define-xwem-deffered
  (deff-name normal-name arglist docstring &rest body)
  "Define new deffered function with function name DEFF-NAME.
Deffered function is function which is called when XEmacs is about to became idle.

Another advantage of deffered function is that only one instance of
function will be called with same arguments.  For example if you have
`my-defffun' deffered function and you call twice `(my-defffun 1)',
`(my-defffun 1)' - then when XEmacs will be about idle only one call
occurs to `my-defffun'.  However if you pass different arguments, all
calls with different arguments are called.  Arguments are equal if
they are either `eq' or both are lists, where each element is `eq'.

NAME, ARGLIST, DOCSTRING and BODY argument have same meaning as for `defun'.
If NORMAL-NAME is specified, also define non-deffered variant of DEFF-NAME function.
If NORMAL-NAME is ommited, then normal-name constructed by
concatination of DEFF-NAME and \"-1\"."
  (unless (and (not (null normal-name))
               (symbolp normal-name))
    ;; If NORMAL-NAME ommited
    (setq body (cons docstring body))
    (setq docstring arglist)
    (setq arglist normal-name)
    (setq normal-name (intern (concat (symbol-name deff-name) "-1"))))

  `(progn
     (defun ,normal-name ,arglist
       ,docstring
       ,@body)

     (defun ,deff-name (&rest args)
       ,(concat "Deffered variant of `" (symbol-name normal-name) "'.")
       (apply (quote xwem-deffered-push) (quote ,normal-name) args))))

(defmacro xwem-deffered-funcall (fun &rest args)
  "Call FUN with ARGS, deffering funcall to FUN."
  `(xwem-deffered-push ,fun ,@args))

(defmacro xwem-unwind-protect (body-form &rest unwind-forms)
  "Execute BODY-FORM protecting it in safe more with UNWIND-FORMS.
`xwem-unwind-protect' differs from `unwind-protect' that
`xwem-unwind-protect' executes UNWIND-FORMS even when debugging."
  `(prog1
    (condition-case xwem-unwind-error
        ,body-form
      (t ,@unwind-forms
         (apply 'error (car xwem-unwind-error) (cdr xwem-unwind-error))))
    ,@unwind-forms))
(put 'xwem-unwind-protect 'lisp-indent-function 1)

(defmacro xwem-overriding-local-map (nlm &rest forms)
  "Execute FORMS installing `xwem-overriding-local-map' to NLM.
Do it in safe manner."
  `(xwem-unwind-protect
       (let ((xwem-override-local-map ,nlm))
         ,@forms)))
(put 'xwem-overriding-local-map 'lisp-indent-function 'defun)

;;; X Properties
(defmacro xwem-XProperty-get (xwin prop-atom-string)
  `(ignore-errors (read (XGetPropertyString
                         (xwem-dpy) ,xwin
                         (XInternAtom (xwem-dpy) ,prop-atom-string)))))
(defmacro xwem-XProperty-set (xwin prop-atom-string prop-val)
  `(if ,prop-val
       (XSetPropertyString (xwem-dpy) ,xwin
                           (XInternAtom (xwem-dpy) ,prop-atom-string)
                           (format "%S" ,prop-val))
     (XDeleteProperty (xwem-dpy) ,xwin
                      (XInternAtom (xwem-dpy) ,prop-atom-string))))


(provide 'xwem-struct)

;;; xwem-struct.el ends here
