added stuff

This commit is contained in:
9ahmed 2021-04-19 02:25:33 +05:00
parent b39b3c4edf
commit f8c1c1396a
259 changed files with 1 additions and 88440 deletions

View File

@ -1 +0,0 @@
Good signature from 066DAFCB81E42C40 GNU ELPA Signing Agent (2019) <elpasign@elpa.gnu.org> (trust undefined) created at 2020-05-25T02:05:01+0500 using RSA

View File

@ -1 +0,0 @@
README.md

File diff suppressed because it is too large Load Diff

View File

@ -1,315 +0,0 @@
;;; exwm-autoloads.el --- automatically extracted autoloads -*- lexical-binding: t -*-
;;
;;; Code:
(add-to-list 'load-path (directory-file-name
(or (file-name-directory #$) (car load-path))))
;;;### (autoloads nil "exwm" "exwm.el" (0 0 0 0))
;;; Generated autoloads from exwm.el
(autoload 'exwm-restart "exwm" "\
Restart EXWM." t nil)
(autoload 'exwm-init "exwm" "\
Initialize EXWM.
\(fn &optional FRAME)" t nil)
(autoload 'exwm-exit "exwm" "\
Exit EXWM." t nil)
(autoload 'exwm-enable "exwm" "\
Enable/Disable EXWM.
\(fn &optional UNDO)" nil nil)
(register-definition-prefixes "exwm" '("exwm-"))
;;;***
;;;### (autoloads nil "exwm-cm" "exwm-cm.el" (0 0 0 0))
;;; Generated autoloads from exwm-cm.el
(register-definition-prefixes "exwm-cm" '("exwm-cm-"))
;;;***
;;;### (autoloads nil "exwm-config" "exwm-config.el" (0 0 0 0))
;;; Generated autoloads from exwm-config.el
(register-definition-prefixes "exwm-config" '("exwm-config-"))
;;;***
;;;### (autoloads nil "exwm-core" "exwm-core.el" (0 0 0 0))
;;; Generated autoloads from exwm-core.el
(register-definition-prefixes "exwm-core" '("exwm-"))
;;;***
;;;### (autoloads nil "exwm-floating" "exwm-floating.el" (0 0 0 0))
;;; Generated autoloads from exwm-floating.el
(autoload 'exwm-floating-toggle-floating "exwm-floating" "\
Toggle the current window between floating and non-floating states." t nil)
(autoload 'exwm-floating-hide "exwm-floating" "\
Hide the current floating X window (which would show again when selected)." t nil)
(register-definition-prefixes "exwm-floating" '("exwm-floating-"))
;;;***
;;;### (autoloads nil "exwm-input" "exwm-input.el" (0 0 0 0))
;;; Generated autoloads from exwm-input.el
(autoload 'exwm-input-set-key "exwm-input" "\
Set a global key binding.
The new key binding only takes effect in real time when this command is
called interactively, and is lost when this session ends unless it's
specifically saved in the Customize interface for `exwm-input-global-keys'.
In configuration you should customize or set `exwm-input-global-keys'
instead.
\(fn KEY COMMAND)" t nil)
(autoload 'exwm-input-grab-keyboard "exwm-input" "\
Switch to line-mode.
\(fn &optional ID)" t nil)
(autoload 'exwm-input-release-keyboard "exwm-input" "\
Switch to char-mode.
\(fn &optional ID)" t nil)
(autoload 'exwm-input-toggle-keyboard "exwm-input" "\
Toggle between 'line-mode' and 'char-mode'.
\(fn &optional ID)" t nil)
(autoload 'exwm-input-send-next-key "exwm-input" "\
Send next key to client window.
EXWM will prompt for the key to send. This command can be prefixed to send
multiple keys. If END-KEY is non-nil, stop sending keys if it's pressed.
\(fn TIMES &optional END-KEY)" t nil)
(autoload 'exwm-input-set-simulation-key "exwm-input" "\
Set a simulation key.
The simulation key takes effect in real time, but is lost when this session
ends unless it's specifically saved in the Customize interface for
`exwm-input-simulation-keys'.
\(fn ORIGINAL-KEY SIMULATED-KEY)" t nil)
(autoload 'exwm-input-send-simulation-key "exwm-input" "\
Fake a key event according to the last input key sequence.
\(fn TIMES)" t nil)
(autoload 'exwm-input-invoke-factory "exwm-input" "\
Make a command that invokes KEYS when called.
One use is to access the keymap bound to KEYS (as prefix keys) in char-mode.
\(fn KEYS)" nil t)
(register-definition-prefixes "exwm-input" '("exwm-input-"))
;;;***
;;;### (autoloads nil "exwm-layout" "exwm-layout.el" (0 0 0 0))
;;; Generated autoloads from exwm-layout.el
(autoload 'exwm-layout-set-fullscreen "exwm-layout" "\
Make window ID fullscreen.
\(fn &optional ID)" t nil)
(autoload 'exwm-layout-unset-fullscreen "exwm-layout" "\
Restore window from fullscreen state.
\(fn &optional ID)" t nil)
(autoload 'exwm-layout-toggle-fullscreen "exwm-layout" "\
Toggle fullscreen mode.
\(fn &optional ID)" t nil)
(autoload 'exwm-layout-enlarge-window "exwm-layout" "\
Make the selected window DELTA pixels taller.
If no argument is given, make the selected window one pixel taller. If the
optional argument HORIZONTAL is non-nil, make selected window DELTA pixels
wider. If DELTA is negative, shrink selected window by -DELTA pixels.
Normal hints are checked and regarded if the selected window is displaying an
`exwm-mode' buffer. However, this may violate the normal hints set on other X
windows.
\(fn DELTA &optional HORIZONTAL)" t nil)
(autoload 'exwm-layout-enlarge-window-horizontally "exwm-layout" "\
Make the selected window DELTA pixels wider.
See also `exwm-layout-enlarge-window'.
\(fn DELTA)" t nil)
(autoload 'exwm-layout-shrink-window "exwm-layout" "\
Make the selected window DELTA pixels lower.
See also `exwm-layout-enlarge-window'.
\(fn DELTA)" t nil)
(autoload 'exwm-layout-shrink-window-horizontally "exwm-layout" "\
Make the selected window DELTA pixels narrower.
See also `exwm-layout-enlarge-window'.
\(fn DELTA)" t nil)
(autoload 'exwm-layout-hide-mode-line "exwm-layout" "\
Hide mode-line." t nil)
(autoload 'exwm-layout-show-mode-line "exwm-layout" "\
Show mode-line." t nil)
(autoload 'exwm-layout-toggle-mode-line "exwm-layout" "\
Toggle the display of mode-line." t nil)
(register-definition-prefixes "exwm-layout" '("exwm-layout-"))
;;;***
;;;### (autoloads nil "exwm-manage" "exwm-manage.el" (0 0 0 0))
;;; Generated autoloads from exwm-manage.el
(register-definition-prefixes "exwm-manage" '("exwm-manage-"))
;;;***
;;;### (autoloads nil "exwm-randr" "exwm-randr.el" (0 0 0 0))
;;; Generated autoloads from exwm-randr.el
(autoload 'exwm-randr-refresh "exwm-randr" "\
Refresh workspaces according to the updated RandR info." t nil)
(register-definition-prefixes "exwm-randr" '("exwm-randr-"))
;;;***
;;;### (autoloads nil "exwm-systemtray" "exwm-systemtray.el" (0 0
;;;;;; 0 0))
;;; Generated autoloads from exwm-systemtray.el
(register-definition-prefixes "exwm-systemtray" '("exwm-systemtray-" "xcb:systemtray:-ClientMessage"))
;;;***
;;;### (autoloads nil "exwm-workspace" "exwm-workspace.el" (0 0 0
;;;;;; 0))
;;; Generated autoloads from exwm-workspace.el
(autoload 'exwm-workspace--get-geometry "exwm-workspace" "\
Return the geometry of frame FRAME.
\(fn FRAME)" nil nil)
(autoload 'exwm-workspace--current-height "exwm-workspace" "\
Return the height of current workspace." nil nil)
(autoload 'exwm-workspace--minibuffer-own-frame-p "exwm-workspace" "\
Reports whether the minibuffer is displayed in its own frame." nil nil)
(autoload 'exwm-workspace-switch "exwm-workspace" "\
Switch to workspace INDEX (0-based).
Query for the index if not specified when called interactively. Passing a
workspace frame as the first option or making use of the rest options are
for internal use only.
\(fn FRAME-OR-INDEX &optional FORCE)" t nil)
(autoload 'exwm-workspace-switch-create "exwm-workspace" "\
Switch to workspace INDEX or creating it first if it does not exist yet.
Passing a workspace frame as the first option is for internal use only.
\(fn FRAME-OR-INDEX)" t nil)
(autoload 'exwm-workspace-swap "exwm-workspace" "\
Interchange position of WORKSPACE1 with that of WORKSPACE2.
\(fn WORKSPACE1 WORKSPACE2)" t nil)
(autoload 'exwm-workspace-move "exwm-workspace" "\
Move WORKSPACE to the NTH position.
When called interactively, prompt for a workspace and move current one just
before it.
\(fn WORKSPACE NTH)" t nil)
(autoload 'exwm-workspace-add "exwm-workspace" "\
Add a workspace as the INDEX-th workspace, or the last one if INDEX is nil.
INDEX must not exceed the current number of workspaces.
\(fn &optional INDEX)" t nil)
(autoload 'exwm-workspace-delete "exwm-workspace" "\
Delete the workspace FRAME-OR-INDEX.
\(fn &optional FRAME-OR-INDEX)" t nil)
(autoload 'exwm-workspace-move-window "exwm-workspace" "\
Move window ID to workspace FRAME-OR-INDEX.
\(fn FRAME-OR-INDEX &optional ID)" t nil)
(autoload 'exwm-workspace-switch-to-buffer "exwm-workspace" "\
Make the current Emacs window display another buffer.
\(fn BUFFER-OR-NAME)" t nil)
(autoload 'exwm-workspace-attach-minibuffer "exwm-workspace" "\
Attach the minibuffer so that it always shows." t nil)
(autoload 'exwm-workspace-detach-minibuffer "exwm-workspace" "\
Detach the minibuffer so that it automatically hides." t nil)
(autoload 'exwm-workspace-toggle-minibuffer "exwm-workspace" "\
Attach the minibuffer if it's detached, or detach it if it's attached." t nil)
(register-definition-prefixes "exwm-workspace" '("exwm-workspace-"))
;;;***
;;;### (autoloads nil "exwm-xim" "exwm-xim.el" (0 0 0 0))
;;; Generated autoloads from exwm-xim.el
(register-definition-prefixes "exwm-xim" '("exwm-xim-"))
;;;***
;;;### (autoloads nil nil ("exwm-pkg.el") (0 0 0 0))
;;;***
;; Local Variables:
;; version-control: never
;; no-byte-compile: t
;; no-update-autoloads: t
;; coding: utf-8
;; End:
;;; exwm-autoloads.el ends here

View File

@ -1,50 +0,0 @@
;;; exwm-cm.el --- Compositing Manager for EXWM -*- lexical-binding: t -*-
;; Copyright (C) 2016-2020 Free Software Foundation, Inc.
;; Author: Chris Feng <chris.w.feng@gmail.com>
;; This file is part of GNU Emacs.
;; GNU Emacs 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 3 of the License, or
;; (at your option) any later version.
;; GNU Emacs 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 GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
;;; Commentary:
;; This module is obsolete since EXWM now supports third-party compositors.
;;; Code:
(make-obsolete-variable 'exwm-cm-opacity
"This variable should no longer be used." "26")
(defun exwm-cm-set-opacity (&rest _args)
(declare (obsolete nil "26")))
(defun exwm-cm-enable ()
(declare (obsolete nil "26")))
(defun exwm-cm-start ()
(declare (obsolete nil "26")))
(defun exwm-cm-stop ()
(declare (obsolete nil "26")))
(defun exwm-cm-toggle ()
(declare (obsolete nil "26")))
(provide 'exwm-cm)
;;; exwm-cm.el ends here

Binary file not shown.

View File

@ -1,131 +0,0 @@
;;; exwm-config.el --- Predefined configurations -*- lexical-binding: t -*-
;; Copyright (C) 2015-2020 Free Software Foundation, Inc.
;; Author: Chris Feng <chris.w.feng@gmail.com>
;; This file is part of GNU Emacs.
;; GNU Emacs 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 3 of the License, or
;; (at your option) any later version.
;; GNU Emacs 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 GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
;;; Commentary:
;; This module contains typical (yet minimal) configurations of EXWM.
;;; Code:
(require 'exwm)
(require 'ido)
(define-obsolete-function-alias 'exwm-config-default
#'exwm-config-example "27.1")
(defun exwm-config-example ()
"Default configuration of EXWM."
;; Set the initial workspace number.
(unless (get 'exwm-workspace-number 'saved-value)
(setq exwm-workspace-number 4))
;; Make class name the buffer name
(add-hook 'exwm-update-class-hook
(lambda ()
(exwm-workspace-rename-buffer exwm-class-name)))
;; Global keybindings.
(unless (get 'exwm-input-global-keys 'saved-value)
(setq exwm-input-global-keys
`(
;; 's-r': Reset (to line-mode).
([?\s-r] . exwm-reset)
;; 's-w': Switch workspace.
([?\s-w] . exwm-workspace-switch)
;; 's-&': Launch application.
([?\s-&] . (lambda (command)
(interactive (list (read-shell-command "$ ")))
(start-process-shell-command command nil command)))
;; 's-N': Switch to certain workspace.
,@(mapcar (lambda (i)
`(,(kbd (format "s-%d" i)) .
(lambda ()
(interactive)
(exwm-workspace-switch-create ,i))))
(number-sequence 0 9)))))
;; Line-editing shortcuts
(unless (get 'exwm-input-simulation-keys 'saved-value)
(setq exwm-input-simulation-keys
'(([?\C-b] . [left])
([?\C-f] . [right])
([?\C-p] . [up])
([?\C-n] . [down])
([?\C-a] . [home])
([?\C-e] . [end])
([?\M-v] . [prior])
([?\C-v] . [next])
([?\C-d] . [delete])
([?\C-k] . [S-end delete]))))
;; Enable EXWM
(exwm-enable)
;; Configure Ido
(exwm-config-ido)
;; Other configurations
(exwm-config-misc))
(defun exwm-config--fix/ido-buffer-window-other-frame ()
"Fix `ido-buffer-window-other-frame'."
(defalias 'exwm-config-ido-buffer-window-other-frame
(symbol-function #'ido-buffer-window-other-frame))
(defun ido-buffer-window-other-frame (buffer)
"This is a version redefined by EXWM.
You can find the original one at `exwm-config-ido-buffer-window-other-frame'."
(with-current-buffer (window-buffer (selected-window))
(if (and (derived-mode-p 'exwm-mode)
exwm--floating-frame)
;; Switch from a floating frame.
(with-current-buffer buffer
(if (and (derived-mode-p 'exwm-mode)
exwm--floating-frame
(eq exwm--frame exwm-workspace--current))
;; Switch to another floating frame.
(frame-root-window exwm--floating-frame)
;; Do not switch if the buffer is not on the current workspace.
(or (get-buffer-window buffer exwm-workspace--current)
(selected-window))))
(with-current-buffer buffer
(when (derived-mode-p 'exwm-mode)
(if (eq exwm--frame exwm-workspace--current)
(when exwm--floating-frame
;; Switch to a floating frame on the current workspace.
(frame-selected-window exwm--floating-frame))
;; Do not switch to exwm-mode buffers on other workspace (which
;; won't work unless `exwm-layout-show-all-buffers' is set)
(unless exwm-layout-show-all-buffers
(selected-window)))))))))
(defun exwm-config-ido ()
"Configure Ido to work with EXWM."
(ido-mode 1)
(add-hook 'exwm-init-hook #'exwm-config--fix/ido-buffer-window-other-frame))
(defun exwm-config-misc ()
"Other configurations."
;; Make more room
(menu-bar-mode -1)
(tool-bar-mode -1)
(scroll-bar-mode -1)
(fringe-mode 1))
(provide 'exwm-config)
;;; exwm-config.el ends here

Binary file not shown.

View File

@ -1,375 +0,0 @@
;;; exwm-core.el --- Core definitions -*- lexical-binding: t -*-
;; Copyright (C) 2015-2020 Free Software Foundation, Inc.
;; Author: Chris Feng <chris.w.feng@gmail.com>
;; This file is part of GNU Emacs.
;; GNU Emacs 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 3 of the License, or
;; (at your option) any later version.
;; GNU Emacs 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 GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
;;; Commentary:
;; This module includes core definitions of variables, macros, functions, etc
;; shared by various other modules.
;;; Code:
(require 'kmacro)
(require 'xcb)
(require 'xcb-icccm)
(require 'xcb-ewmh)
(require 'xcb-debug)
(defcustom exwm-debug-log-time-function #'exwm-debug-log-uptime
"Function used for generating timestamps in `exwm-debug' logs.
Here are some predefined candidates:
`exwm-debug-log-uptime': Display the uptime of this Emacs instance.
`exwm-debug-log-time': Display time of day.
`nil': Disable timestamp."
:group 'exwm-debug
:type `(choice (const :tag "Emacs uptime" ,#'exwm-debug-log-uptime)
(const :tag "Time of day" ,#'exwm-debug-log-time)
(const :tag "Off" nil)
(function :tag "Other"))
:set (lambda (symbol value)
(set-default symbol value)
;; Also change the format for XELB to make logs consistent
;; (as they share the same buffer).
(setq xcb-debug:log-time-function value)))
(defalias 'exwm-debug-log-uptime 'xcb-debug:log-uptime
"Add uptime to `exwm-debug' logs.")
(defalias 'exwm-debug-log-time 'xcb-debug:log-time
"Add time of day to `exwm-debug' logs.")
(defvar exwm--connection nil "X connection.")
(defvar exwm--wmsn-window nil
"An X window owning the WM_S0 selection.")
(defvar exwm--wmsn-acquire-timeout 3
"Number of seconds to wait for other window managers to release the selection.")
(defvar exwm--guide-window nil
"An X window separating workspaces and X windows.")
(defvar exwm--id-buffer-alist nil "Alist of (<X window ID> . <Emacs buffer>).")
(defvar exwm--root nil "Root window.")
(defvar exwm-input--global-prefix-keys)
(defvar exwm-input--simulation-keys)
(defvar exwm-input-line-mode-passthrough)
(defvar exwm-input-prefix-keys)
(declare-function exwm-input--fake-key "exwm-input.el" (event))
(declare-function exwm-input--on-KeyPress-line-mode "exwm-input.el"
(key-press raw-data))
(declare-function exwm-floating-hide "exwm-floating.el")
(declare-function exwm-floating-toggle-floating "exwm-floating.el")
(declare-function exwm-input-release-keyboard "exwm-input.el")
(declare-function exwm-input-send-next-key "exwm-input.el" (times))
(declare-function exwm-layout-set-fullscreen "exwm-layout.el" (&optional id))
(declare-function exwm-layout-toggle-mode-line "exwm-layout.el")
(declare-function exwm-manage--kill-buffer-query-function "exwm-manage.el")
(declare-function exwm-workspace-move-window "exwm-workspace.el"
(frame-or-index &optional id))
(define-minor-mode exwm-debug
"Debug-logging enabled if non-nil"
:global t)
(defmacro exwm--debug (&rest forms)
(when exwm-debug `(progn ,@forms)))
(defmacro exwm--log (&optional format-string &rest objects)
"Emit a message prepending the name of the function being executed.
FORMAT-STRING is a string specifying the message to output, as in
`format'. The OBJECTS arguments specify the substitutions."
(unless format-string (setq format-string ""))
`(when exwm-debug
(xcb-debug:message ,(concat "%s%s:\t" format-string "\n")
(if exwm-debug-log-time-function
(funcall exwm-debug-log-time-function)
"")
(xcb-debug:compile-time-function-name)
,@objects)
nil))
(defsubst exwm--id->buffer (id)
"X window ID => Emacs buffer."
(cdr (assoc id exwm--id-buffer-alist)))
(defsubst exwm--buffer->id (buffer)
"Emacs buffer BUFFER => X window ID."
(car (rassoc buffer exwm--id-buffer-alist)))
(defun exwm--lock (&rest _args)
"Lock (disable all events)."
(exwm--log)
(xcb:+request exwm--connection
(make-instance 'xcb:ChangeWindowAttributes
:window exwm--root
:value-mask xcb:CW:EventMask
:event-mask xcb:EventMask:NoEvent))
(xcb:flush exwm--connection))
(defun exwm--unlock (&rest _args)
"Unlock (enable all events)."
(exwm--log)
(xcb:+request exwm--connection
(make-instance 'xcb:ChangeWindowAttributes
:window exwm--root
:value-mask xcb:CW:EventMask
:event-mask (eval-when-compile
(logior xcb:EventMask:SubstructureRedirect
xcb:EventMask:StructureNotify))))
(xcb:flush exwm--connection))
(defun exwm--set-geometry (xwin x y width height)
"Set the geometry of X window XWIN to WIDTHxHEIGHT+X+Y.
Nil can be passed as placeholder."
(exwm--log "Setting #x%x to %sx%s+%s+%s" xwin width height x y)
(xcb:+request exwm--connection
(make-instance 'xcb:ConfigureWindow
:window xwin
:value-mask (logior (if x xcb:ConfigWindow:X 0)
(if y xcb:ConfigWindow:Y 0)
(if width xcb:ConfigWindow:Width 0)
(if height xcb:ConfigWindow:Height 0))
:x x :y y :width width :height height)))
(defun exwm--intern-atom (atom)
"Intern X11 ATOM."
(slot-value (xcb:+request-unchecked+reply exwm--connection
(make-instance 'xcb:InternAtom
:only-if-exists 0
:name-len (length atom)
:name atom))
'atom))
(defmacro exwm--defer (secs function &rest args)
"Defer the execution of FUNCTION.
The action is to call FUNCTION with arguments ARGS. If Emacs is not idle,
defer the action until Emacs is idle. Otherwise, defer the action until at
least SECS seconds later."
`(run-with-idle-timer (+ (float-time (or (current-idle-time)
(seconds-to-time (- ,secs))))
,secs)
nil
,function
,@args))
(defun exwm--get-client-event-mask ()
"Return event mask set on all managed windows."
(logior xcb:EventMask:StructureNotify
xcb:EventMask:PropertyChange
(if mouse-autoselect-window
xcb:EventMask:EnterWindow 0)))
(defun exwm--color->pixel (color)
"Convert COLOR to PIXEL (index in TrueColor colormap)."
(when (and color
(eq (x-display-visual-class) 'true-color))
(let ((rgb (x-color-values color)))
(logior (lsh (lsh (pop rgb) -8) 16)
(lsh (lsh (pop rgb) -8) 8)
(lsh (pop rgb) -8)))))
;; Internal variables
(defvar-local exwm--id nil) ;window ID
(defvar-local exwm--configurations nil) ;initial configurations.
(defvar-local exwm--frame nil) ;workspace frame
(defvar-local exwm--floating-frame nil) ;floating frame
(defvar-local exwm--mode-line-format nil) ;save mode-line-format
(defvar-local exwm--floating-frame-position nil) ;set when hidden.
(defvar-local exwm--fixed-size nil) ;fixed size
(defvar-local exwm--selected-input-mode 'line-mode
"Input mode as selected by the user.
One of `line-mode' or `char-mode'.")
(defvar-local exwm--input-mode 'line-mode
"Actual input mode, i.e. whether mouse and keyboard are grabbed.")
;; Properties
(defvar-local exwm--desktop nil "_NET_WM_DESKTOP.")
(defvar-local exwm-window-type nil "_NET_WM_WINDOW_TYPE.")
(defvar-local exwm--geometry nil)
(defvar-local exwm-class-name nil "Class name in WM_CLASS.")
(defvar-local exwm-instance-name nil "Instance name in WM_CLASS.")
(defvar-local exwm-title nil "Window title (either _NET_WM_NAME or WM_NAME)")
(defvar-local exwm--title-is-utf8 nil)
(defvar-local exwm-transient-for nil "WM_TRANSIENT_FOR.")
(defvar-local exwm--protocols nil)
(defvar-local exwm-state xcb:icccm:WM_STATE:NormalState "WM_STATE.")
(defvar-local exwm--ewmh-state nil "_NET_WM_STATE.")
;; _NET_WM_NORMAL_HINTS
(defvar-local exwm--normal-hints-x nil)
(defvar-local exwm--normal-hints-y nil)
(defvar-local exwm--normal-hints-width nil)
(defvar-local exwm--normal-hints-height nil)
(defvar-local exwm--normal-hints-min-width nil)
(defvar-local exwm--normal-hints-min-height nil)
(defvar-local exwm--normal-hints-max-width nil)
(defvar-local exwm--normal-hints-max-height nil)
;; (defvar-local exwm--normal-hints-win-gravity nil)
;; WM_HINTS
(defvar-local exwm--hints-input nil)
(defvar-local exwm--hints-urgency nil)
;; _MOTIF_WM_HINTS
(defvar-local exwm--mwm-hints-decorations t)
(defvar exwm-mode-map
(let ((map (make-sparse-keymap)))
(define-key map "\C-c\C-d\C-l" #'xcb-debug:clear)
(define-key map "\C-c\C-d\C-m" #'xcb-debug:mark)
(define-key map "\C-c\C-d\C-t" #'exwm-debug)
(define-key map "\C-c\C-f" #'exwm-layout-set-fullscreen)
(define-key map "\C-c\C-h" #'exwm-floating-hide)
(define-key map "\C-c\C-k" #'exwm-input-release-keyboard)
(define-key map "\C-c\C-m" #'exwm-workspace-move-window)
(define-key map "\C-c\C-q" #'exwm-input-send-next-key)
(define-key map "\C-c\C-t\C-f" #'exwm-floating-toggle-floating)
(define-key map "\C-c\C-t\C-m" #'exwm-layout-toggle-mode-line)
map)
"Keymap for `exwm-mode'.")
(defvar exwm--kmacro-map
(let ((map (make-sparse-keymap)))
(define-key map [t]
(lambda ()
(interactive)
(cond
((or exwm-input-line-mode-passthrough
;; Do not test `exwm-input--during-command'.
(active-minibuffer-window)
(memq last-input-event exwm-input--global-prefix-keys)
(memq last-input-event exwm-input-prefix-keys)
(lookup-key exwm-mode-map (vector last-input-event))
(gethash last-input-event exwm-input--simulation-keys))
(set-transient-map (make-composed-keymap (list exwm-mode-map
global-map)))
(push last-input-event unread-command-events))
(t
(exwm-input--fake-key last-input-event)))))
map)
"Keymap used when executing keyboard macros.")
;; This menu mainly acts as an reminder for users. Thus it should be as
;; detailed as possible, even some entries do not make much sense here.
;; Also, inactive entries should be disabled rather than hidden.
(easy-menu-define exwm-mode-menu exwm-mode-map
"Menu for `exwm-mode'."
'("EXWM"
"---"
"*General*"
"---"
["Toggle floating" exwm-floating-toggle-floating]
["Toggle fullscreen mode" exwm-layout-toggle-fullscreen]
["Hide window" exwm-floating-hide exwm--floating-frame]
["Close window" (kill-buffer (current-buffer))]
"---"
"*Resizing*"
"---"
["Toggle mode-line" exwm-layout-toggle-mode-line]
["Enlarge window vertically" exwm-layout-enlarge-window]
["Enlarge window horizontally" exwm-layout-enlarge-window-horizontally]
["Shrink window vertically" exwm-layout-shrink-window]
["Shrink window horizontally" exwm-layout-shrink-window-horizontally]
"---"
"*Keyboard*"
"---"
["Toggle keyboard mode" exwm-input-toggle-keyboard]
["Send key" exwm-input-send-next-key (eq exwm--input-mode 'line-mode)]
;; This is merely a reference.
("Send simulation key" :filter
(lambda (&rest _args)
(let (result)
(maphash
(lambda (key value)
(when (sequencep key)
(setq result (append result
`([
,(format "Send '%s'"
(key-description value))
(lambda ()
(interactive)
(dolist (i ',value)
(exwm-input--fake-key i)))
:keys ,(key-description key)])))))
exwm-input--simulation-keys)
result)))
["Define global binding" exwm-input-set-key]
"---"
"*Workspace*"
"---"
["Add workspace" exwm-workspace-add]
["Delete current workspace" exwm-workspace-delete]
["Move workspace to" exwm-workspace-move]
["Swap workspaces" exwm-workspace-swap]
["Move X window to" exwm-workspace-move-window]
["Move X window from" exwm-workspace-switch-to-buffer]
["Toggle minibuffer" exwm-workspace-toggle-minibuffer]
["Switch workspace" exwm-workspace-switch]
;; Place this entry at bottom to avoid selecting others by accident.
("Switch to" :filter
(lambda (&rest _args)
(mapcar (lambda (i)
`[,(format "Workspace %d" i)
(lambda ()
(interactive)
(exwm-workspace-switch ,i))
(/= ,i exwm-workspace-current-index)])
(number-sequence 0 (1- (exwm-workspace--count))))))))
(define-derived-mode exwm-mode nil "EXWM"
"Major mode for managing X windows.
\\{exwm-mode-map}"
;;
(setq mode-name
'(:eval (propertize "EXWM" 'face
(when (cl-some (lambda (i)
(frame-parameter i 'exwm-urgency))
exwm-workspace--list)
'font-lock-warning-face))))
;; Change major-mode is not allowed
(add-hook 'change-major-mode-hook #'kill-buffer nil t)
;; Kill buffer -> close window
(add-hook 'kill-buffer-query-functions
#'exwm-manage--kill-buffer-query-function nil t)
;; Redirect events when executing keyboard macros.
(push `(executing-kbd-macro . ,exwm--kmacro-map)
minor-mode-overriding-map-alist)
(setq buffer-read-only t
cursor-type nil
left-margin-width nil
right-margin-width nil
left-fringe-width 0
right-fringe-width 0
vertical-scroll-bar nil))
(provide 'exwm-core)
;;; exwm-core.el ends here

Binary file not shown.

View File

@ -1,783 +0,0 @@
;;; exwm-floating.el --- Floating Module for EXWM -*- lexical-binding: t -*-
;; Copyright (C) 2015-2020 Free Software Foundation, Inc.
;; Author: Chris Feng <chris.w.feng@gmail.com>
;; This file is part of GNU Emacs.
;; GNU Emacs 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 3 of the License, or
;; (at your option) any later version.
;; GNU Emacs 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 GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
;;; Commentary:
;; This module deals with the conversion between floating and non-floating
;; states and implements moving/resizing operations on floating windows.
;;; Code:
(require 'xcb-cursor)
(require 'exwm-core)
(defgroup exwm-floating nil
"Floating."
:version "25.3"
:group 'exwm)
(defcustom exwm-floating-setup-hook nil
"Normal hook run when an X window has been made floating, in the
context of the corresponding buffer."
:type 'hook)
(defcustom exwm-floating-exit-hook nil
"Normal hook run when an X window has exited floating state, in the
context of the corresponding buffer."
:type 'hook)
(defcustom exwm-floating-border-color "navy"
"Border color of floating windows."
:type 'color
:initialize #'custom-initialize-default
:set (lambda (symbol value)
(set-default symbol value)
;; Change border color for all floating X windows.
(when exwm--connection
(let ((border-pixel (exwm--color->pixel value)))
(when border-pixel
(dolist (pair exwm--id-buffer-alist)
(with-current-buffer (cdr pair)
(when exwm--floating-frame
(xcb:+request exwm--connection
(make-instance 'xcb:ChangeWindowAttributes
:window
(frame-parameter exwm--floating-frame
'exwm-container)
:value-mask xcb:CW:BorderPixel
:border-pixel border-pixel)))))
(xcb:flush exwm--connection))))))
(defcustom exwm-floating-border-width 1
"Border width of floating windows."
:type '(integer
:validate (lambda (widget)
(when (< (widget-value widget) 0)
(widget-put widget :error "Border width is at least 0")
widget)))
:initialize #'custom-initialize-default
:set (lambda (symbol value)
(let ((delta (- value exwm-floating-border-width))
container)
(set-default symbol value)
;; Change border width for all floating X windows.
(dolist (pair exwm--id-buffer-alist)
(with-current-buffer (cdr pair)
(when exwm--floating-frame
(setq container (frame-parameter exwm--floating-frame
'exwm-container))
(with-slots (x y)
(xcb:+request-unchecked+reply exwm--connection
(make-instance 'xcb:GetGeometry
:drawable container))
(xcb:+request exwm--connection
(make-instance 'xcb:ConfigureWindow
:window container
:value-mask
(logior xcb:ConfigWindow:X
xcb:ConfigWindow:Y
xcb:ConfigWindow:BorderWidth)
:border-width value
:x (- x delta)
:y (- y delta)))))))
(when exwm--connection
(xcb:flush exwm--connection)))))
;; Cursors for moving/resizing a window
(defvar exwm-floating--cursor-move nil)
(defvar exwm-floating--cursor-top-left nil)
(defvar exwm-floating--cursor-top nil)
(defvar exwm-floating--cursor-top-right nil)
(defvar exwm-floating--cursor-right nil)
(defvar exwm-floating--cursor-bottom-right nil)
(defvar exwm-floating--cursor-bottom nil)
(defvar exwm-floating--cursor-bottom-left nil)
(defvar exwm-floating--cursor-left nil)
(defvar exwm-floating--moveresize-calculate nil
"Calculate move/resize parameters [buffer event-mask x y width height].")
(defvar exwm-workspace--current)
(defvar exwm-workspace--frame-y-offset)
(defvar exwm-workspace--window-y-offset)
(defvar exwm-workspace--workareas)
(declare-function exwm-layout--hide "exwm-layout.el" (id))
(declare-function exwm-layout--iconic-state-p "exwm-layout.el" (&optional id))
(declare-function exwm-layout--refresh "exwm-layout.el" ())
(declare-function exwm-layout--show "exwm-layout.el" (id &optional window))
(declare-function exwm-workspace--position "exwm-workspace.el" (frame))
(declare-function exwm-workspace--update-offsets "exwm-workspace.el" ())
(defun exwm-floating--set-allowed-actions (id tilling)
"Set _NET_WM_ALLOWED_ACTIONS."
(exwm--log "#x%x" id)
(xcb:+request exwm--connection
(make-instance 'xcb:ewmh:set-_NET_WM_ALLOWED_ACTIONS
:window id
:data (if tilling
(vector xcb:Atom:_NET_WM_ACTION_MINIMIZE
xcb:Atom:_NET_WM_ACTION_FULLSCREEN
xcb:Atom:_NET_WM_ACTION_CHANGE_DESKTOP
xcb:Atom:_NET_WM_ACTION_CLOSE)
(vector xcb:Atom:_NET_WM_ACTION_MOVE
xcb:Atom:_NET_WM_ACTION_RESIZE
xcb:Atom:_NET_WM_ACTION_MINIMIZE
xcb:Atom:_NET_WM_ACTION_FULLSCREEN
xcb:Atom:_NET_WM_ACTION_CHANGE_DESKTOP
xcb:Atom:_NET_WM_ACTION_CLOSE)))))
(defun exwm-floating--set-floating (id)
"Make window ID floating."
(let ((window (get-buffer-window (exwm--id->buffer id))))
(when window
;; Hide the non-floating X window first.
(set-window-buffer window (other-buffer nil t))))
(let* ((original-frame (buffer-local-value 'exwm--frame
(exwm--id->buffer id)))
;; Create new frame
(frame (with-current-buffer
(or (get-buffer "*scratch*")
(progn
(set-buffer-major-mode
(get-buffer-create "*scratch*"))
(get-buffer "*scratch*")))
(make-frame
`((minibuffer . ,(minibuffer-window exwm--frame))
(left . ,(* window-min-width -10000))
(top . ,(* window-min-height -10000))
(width . ,window-min-width)
(height . ,window-min-height)
(unsplittable . t))))) ;and fix the size later
(outer-id (string-to-number (frame-parameter frame 'outer-window-id)))
(window-id (string-to-number (frame-parameter frame 'window-id)))
(frame-container (xcb:generate-id exwm--connection))
(window (frame-first-window frame)) ;and it's the only window
(x (slot-value exwm--geometry 'x))
(y (slot-value exwm--geometry 'y))
(width (slot-value exwm--geometry 'width))
(height (slot-value exwm--geometry 'height)))
;; Force drawing menu-bar & tool-bar.
(redisplay t)
(exwm-workspace--update-offsets)
(exwm--log "Floating geometry (original): %dx%d%+d%+d" width height x y)
;; Save frame parameters.
(set-frame-parameter frame 'exwm-outer-id outer-id)
(set-frame-parameter frame 'exwm-id window-id)
(set-frame-parameter frame 'exwm-container frame-container)
;; Fix illegal parameters
;; FIXME: check normal hints restrictions
(let* ((workarea (elt exwm-workspace--workareas
(exwm-workspace--position original-frame)))
(x* (aref workarea 0))
(y* (aref workarea 1))
(width* (aref workarea 2))
(height* (aref workarea 3)))
;; Center floating windows
(when (and (or (= x 0) (= x x*))
(or (= y 0) (= y y*)))
(let ((buffer (exwm--id->buffer exwm-transient-for))
window edges)
(when (and buffer (setq window (get-buffer-window buffer)))
(setq edges (window-inside-absolute-pixel-edges window))
(unless (and (<= width (- (elt edges 2) (elt edges 0)))
(<= height (- (elt edges 3) (elt edges 1))))
(setq edges nil)))
(if edges
;; Put at the center of leading window
(setq x (+ x* (/ (- (elt edges 2) (elt edges 0) width) 2))
y (+ y* (/ (- (elt edges 3) (elt edges 1) height) 2)))
;; Put at the center of screen
(setq x (/ (- width* width) 2)
y (/ (- height* height) 2)))))
(if (> width width*)
;; Too wide
(progn (setq x x*
width width*))
;; Invalid width
(when (= 0 width) (setq width (/ width* 2)))
;; Make sure at least half of the window is visible
(unless (< x* (+ x (/ width 2)) (+ x* width*))
(setq x (+ x* (/ (- width* width) 2)))))
(if (> height height*)
;; Too tall
(setq y y*
height height*)
;; Invalid height
(when (= 0 height) (setq height (/ height* 2)))
;; Make sure at least half of the window is visible
(unless (< y* (+ y (/ height 2)) (+ y* height*))
(setq y (+ y* (/ (- height* height) 2)))))
;; The geometry can be overridden by user options.
(let ((x** (plist-get exwm--configurations 'x))
(y** (plist-get exwm--configurations 'y))
(width** (plist-get exwm--configurations 'width))
(height** (plist-get exwm--configurations 'height)))
(if (integerp x**)
(setq x (+ x* x**))
(when (and (floatp x**)
(>= 1 x** 0))
(setq x (+ x* (round (* x** width*))))))
(if (integerp y**)
(setq y (+ y* y**))
(when (and (floatp y**)
(>= 1 y** 0))
(setq y (+ y* (round (* y** height*))))))
(if (integerp width**)
(setq width width**)
(when (and (floatp width**)
(> 1 width** 0))
(setq width (max 1 (round (* width** width*))))))
(if (integerp height**)
(setq height height**)
(when (and (floatp height**)
(> 1 height** 0))
(setq height (max 1 (round (* height** height*))))))))
(exwm--set-geometry id x y nil nil)
(xcb:flush exwm--connection)
(exwm--log "Floating geometry (corrected): %dx%d%+d%+d" width height x y)
;; Fit frame to client
;; It seems we have to make the frame invisible in order to resize it
;; timely.
;; The frame will be made visible by `select-frame-set-input-focus'.
(make-frame-invisible frame)
(let* ((edges (window-inside-pixel-edges window))
(frame-width (+ width (- (frame-pixel-width frame)
(- (elt edges 2) (elt edges 0)))))
(frame-height (+ height (- (frame-pixel-height frame)
(- (elt edges 3) (elt edges 1)))
;; Use `frame-outer-height' in the future.
exwm-workspace--frame-y-offset))
(floating-mode-line (plist-get exwm--configurations
'floating-mode-line))
(floating-header-line (plist-get exwm--configurations
'floating-header-line))
(border-pixel (exwm--color->pixel exwm-floating-border-color)))
(if floating-mode-line
(setq exwm--mode-line-format (or exwm--mode-line-format
mode-line-format)
mode-line-format floating-mode-line)
(if (and (not (plist-member exwm--configurations 'floating-mode-line))
exwm--mwm-hints-decorations)
(when exwm--mode-line-format
(setq mode-line-format exwm--mode-line-format))
;; The mode-line need to be hidden in floating mode.
(setq frame-height (- frame-height (window-mode-line-height
(frame-root-window frame)))
exwm--mode-line-format (or exwm--mode-line-format
mode-line-format)
mode-line-format nil)))
(if floating-header-line
(setq header-line-format floating-header-line)
(if (and (not (plist-member exwm--configurations
'floating-header-line))
exwm--mwm-hints-decorations)
(setq header-line-format nil)
;; The header-line need to be hidden in floating mode.
(setq frame-height (- frame-height (window-header-line-height
(frame-root-window frame)))
header-line-format nil)))
(set-frame-size frame frame-width frame-height t)
;; Create the frame container as the parent of the frame.
(xcb:+request exwm--connection
(make-instance 'xcb:CreateWindow
:depth 0
:wid frame-container
:parent exwm--root
:x x
:y (- y exwm-workspace--window-y-offset)
:width width
:height height
:border-width
(with-current-buffer (exwm--id->buffer id)
(let ((border-witdh (plist-get exwm--configurations
'border-width)))
(if (and (integerp border-witdh)
(>= border-witdh 0))
border-witdh
exwm-floating-border-width)))
:class xcb:WindowClass:InputOutput
:visual 0
:value-mask (logior xcb:CW:BackPixmap
(if border-pixel
xcb:CW:BorderPixel 0)
xcb:CW:OverrideRedirect)
:background-pixmap xcb:BackPixmap:ParentRelative
:border-pixel border-pixel
:override-redirect 1))
(xcb:+request exwm--connection
(make-instance 'xcb:ewmh:set-_NET_WM_NAME
:window frame-container
:data
(format "EXWM floating frame container for 0x%x" id)))
;; Map it.
(xcb:+request exwm--connection
(make-instance 'xcb:MapWindow :window frame-container))
;; Put the X window right above this frame container.
(xcb:+request exwm--connection
(make-instance 'xcb:ConfigureWindow
:window id
:value-mask (logior xcb:ConfigWindow:Sibling
xcb:ConfigWindow:StackMode)
:sibling frame-container
:stack-mode xcb:StackMode:Above)))
;; Reparent this frame to its container.
(xcb:+request exwm--connection
(make-instance 'xcb:ReparentWindow
:window outer-id :parent frame-container :x 0 :y 0))
(exwm-floating--set-allowed-actions id nil)
(xcb:flush exwm--connection)
;; Set window/buffer
(with-current-buffer (exwm--id->buffer id)
(setq window-size-fixed exwm--fixed-size
exwm--floating-frame frame)
;; Do the refresh manually.
(remove-hook 'window-configuration-change-hook #'exwm-layout--refresh)
(set-window-buffer window (current-buffer)) ;this changes current buffer
(add-hook 'window-configuration-change-hook #'exwm-layout--refresh)
(set-window-dedicated-p window t)
(exwm-layout--show id window))
(with-current-buffer (exwm--id->buffer id)
(if (exwm-layout--iconic-state-p id)
;; Hide iconic floating X windows.
(exwm-floating-hide)
(with-selected-frame exwm--frame
(exwm-layout--refresh)))
(select-frame-set-input-focus frame))
;; FIXME: Strangely, the Emacs frame can move itself at this point
;; when there are left/top struts set. Force resetting its
;; position seems working, but it'd better to figure out why.
;; FIXME: This also happens in another case (#220) where the cause is
;; still unclear.
(exwm--set-geometry outer-id 0 0 nil nil)
(xcb:flush exwm--connection))
(with-current-buffer (exwm--id->buffer id)
(run-hooks 'exwm-floating-setup-hook))
;; Redraw the frame.
(redisplay t))
(defun exwm-floating--unset-floating (id)
"Make window ID non-floating."
(exwm--log "#x%x" id)
(let ((buffer (exwm--id->buffer id)))
(with-current-buffer buffer
(when exwm--floating-frame
;; The X window is already mapped.
;; Unmap the X window.
(xcb:+request exwm--connection
(make-instance 'xcb:ChangeWindowAttributes
:window id :value-mask xcb:CW:EventMask
:event-mask xcb:EventMask:NoEvent))
(xcb:+request exwm--connection
(make-instance 'xcb:UnmapWindow :window id))
(xcb:+request exwm--connection
(make-instance 'xcb:ChangeWindowAttributes
:window id :value-mask xcb:CW:EventMask
:event-mask (exwm--get-client-event-mask)))
;; Reparent the floating frame back to the root window.
(let ((frame-id (frame-parameter exwm--floating-frame 'exwm-outer-id))
(frame-container (frame-parameter exwm--floating-frame
'exwm-container)))
(xcb:+request exwm--connection
(make-instance 'xcb:UnmapWindow :window frame-id))
(xcb:+request exwm--connection
(make-instance 'xcb:ReparentWindow
:window frame-id
:parent exwm--root
:x 0 :y 0))
;; Also destroy its container.
(xcb:+request exwm--connection
(make-instance 'xcb:DestroyWindow :window frame-container))))
;; Place the X window just above the reference X window.
;; (the stacking order won't change from now on).
;; Also hide the possible floating border.
(xcb:+request exwm--connection
(make-instance 'xcb:ConfigureWindow
:window id
:value-mask (logior xcb:ConfigWindow:BorderWidth
xcb:ConfigWindow:Sibling
xcb:ConfigWindow:StackMode)
:border-width 0
:sibling exwm--guide-window
:stack-mode xcb:StackMode:Above)))
(exwm-floating--set-allowed-actions id t)
(xcb:flush exwm--connection)
(with-current-buffer buffer
(when exwm--floating-frame ;from floating to non-floating
(set-window-dedicated-p (frame-first-window exwm--floating-frame) nil)
;; Select a tiling window and delete the old frame.
(select-window (frame-selected-window exwm-workspace--current))
(with-current-buffer buffer
(delete-frame exwm--floating-frame))))
(with-current-buffer buffer
(setq window-size-fixed nil
exwm--floating-frame nil)
(if (not (plist-member exwm--configurations 'tiling-mode-line))
(when exwm--mode-line-format
(setq mode-line-format exwm--mode-line-format))
(setq exwm--mode-line-format (or exwm--mode-line-format
mode-line-format)
mode-line-format (plist-get exwm--configurations
'tiling-mode-line)))
(if (not (plist-member exwm--configurations 'tiling-header-line))
(setq header-line-format nil)
(setq header-line-format (plist-get exwm--configurations
'tiling-header-line))))
;; Only show X windows in normal state.
(unless (exwm-layout--iconic-state-p)
(pop-to-buffer-same-window buffer)))
(with-current-buffer (exwm--id->buffer id)
(run-hooks 'exwm-floating-exit-hook)))
;;;###autoload
(cl-defun exwm-floating-toggle-floating ()
"Toggle the current window between floating and non-floating states."
(interactive)
(exwm--log)
(unless (derived-mode-p 'exwm-mode)
(cl-return-from exwm-floating-toggle-floating))
(with-current-buffer (window-buffer)
(if exwm--floating-frame
(exwm-floating--unset-floating exwm--id)
(exwm-floating--set-floating exwm--id))))
;;;###autoload
(defun exwm-floating-hide ()
"Hide the current floating X window (which would show again when selected)."
(interactive)
(exwm--log)
(when (and (derived-mode-p 'exwm-mode)
exwm--floating-frame)
(exwm-layout--hide exwm--id)
(select-frame-set-input-focus exwm-workspace--current)))
(defun exwm-floating--start-moveresize (id &optional type)
"Start move/resize."
(exwm--log "#x%x" id)
(let ((buffer-or-id (or (exwm--id->buffer id) id))
frame container-or-id x y width height cursor)
(if (bufferp buffer-or-id)
;; Managed.
(with-current-buffer buffer-or-id
(setq frame exwm--floating-frame
container-or-id (frame-parameter exwm--floating-frame
'exwm-container)))
;; Unmanaged.
(setq container-or-id id))
(when (and container-or-id
;; Test if the pointer can be grabbed
(= xcb:GrabStatus:Success
(slot-value
(xcb:+request-unchecked+reply exwm--connection
(make-instance 'xcb:GrabPointer
:owner-events 0
:grab-window container-or-id
:event-mask xcb:EventMask:NoEvent
:pointer-mode xcb:GrabMode:Async
:keyboard-mode xcb:GrabMode:Async
:confine-to xcb:Window:None
:cursor xcb:Cursor:None
:time xcb:Time:CurrentTime))
'status)))
(with-slots (root-x root-y win-x win-y)
(xcb:+request-unchecked+reply exwm--connection
(make-instance 'xcb:QueryPointer :window id))
(if (not (bufferp buffer-or-id))
;; Unmanaged.
(unless (eq type xcb:ewmh:_NET_WM_MOVERESIZE_MOVE)
(with-slots ((width* width)
(height* height))
(xcb:+request-unchecked+reply exwm--connection
(make-instance 'xcb:GetGeometry :drawable id))
(setq width width*
height height*)))
;; Managed.
(select-window (frame-first-window frame)) ;transfer input focus
(setq width (frame-pixel-width frame)
height (frame-pixel-height frame))
(unless type
;; Determine the resize type according to the pointer position
;; Clicking the center 1/3 part to resize has no effect
(setq x (/ (* 3 win-x) (float width))
y (/ (* 3 win-y) (float height))
type (cond ((and (< x 1) (< y 1))
xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_TOPLEFT)
((and (> x 2) (< y 1))
xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_TOPRIGHT)
((and (> x 2) (> y 2))
xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT)
((and (< x 1) (> y 2))
xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT)
((> x 2) xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_RIGHT)
((> y 2) xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_BOTTOM)
((< x 1) xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_LEFT)
((< y 1) xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_TOP)))))
(if (not type)
(exwm-floating--stop-moveresize)
(cond ((= type xcb:ewmh:_NET_WM_MOVERESIZE_MOVE)
(setq cursor exwm-floating--cursor-move
exwm-floating--moveresize-calculate
(lambda (x y)
(vector buffer-or-id
(eval-when-compile
(logior xcb:ConfigWindow:X
xcb:ConfigWindow:Y))
(- x win-x) (- y win-y) 0 0))))
((= type xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_TOPLEFT)
(setq cursor exwm-floating--cursor-top-left
exwm-floating--moveresize-calculate
(lambda (x y)
(vector buffer-or-id
(eval-when-compile
(logior xcb:ConfigWindow:X
xcb:ConfigWindow:Y
xcb:ConfigWindow:Width
xcb:ConfigWindow:Height))
(- x win-x) (- y win-y)
(- (+ root-x width) x)
(- (+ root-y height) y)))))
((= type xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_TOP)
(setq cursor exwm-floating--cursor-top
exwm-floating--moveresize-calculate
(lambda (_x y)
(vector buffer-or-id
(eval-when-compile
(logior xcb:ConfigWindow:Y
xcb:ConfigWindow:Height))
0 (- y win-y) 0 (- (+ root-y height) y)))))
((= type xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_TOPRIGHT)
(setq cursor exwm-floating--cursor-top-right
exwm-floating--moveresize-calculate
(lambda (x y)
(vector buffer-or-id
(eval-when-compile
(logior xcb:ConfigWindow:Y
xcb:ConfigWindow:Width
xcb:ConfigWindow:Height))
0 (- y win-y) (- x (- root-x width))
(- (+ root-y height) y)))))
((= type xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_RIGHT)
(setq cursor exwm-floating--cursor-right
exwm-floating--moveresize-calculate
(lambda (x _y)
(vector buffer-or-id
xcb:ConfigWindow:Width
0 0 (- x (- root-x width)) 0))))
((= type xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT)
(setq cursor exwm-floating--cursor-bottom-right
exwm-floating--moveresize-calculate
(lambda (x y)
(vector buffer-or-id
(eval-when-compile
(logior xcb:ConfigWindow:Width
xcb:ConfigWindow:Height))
0 0 (- x (- root-x width))
(- y (- root-y height))))))
((= type xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_BOTTOM)
(setq cursor exwm-floating--cursor-bottom
exwm-floating--moveresize-calculate
(lambda (_x y)
(vector buffer-or-id
xcb:ConfigWindow:Height
0 0 0 (- y (- root-y height))))))
((= type xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT)
(setq cursor exwm-floating--cursor-bottom-left
exwm-floating--moveresize-calculate
(lambda (x y)
(vector buffer-or-id
(eval-when-compile
(logior xcb:ConfigWindow:X
xcb:ConfigWindow:Width
xcb:ConfigWindow:Height))
(- x win-x)
0
(- (+ root-x width) x)
(- y (- root-y height))))))
((= type xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_LEFT)
(setq cursor exwm-floating--cursor-left
exwm-floating--moveresize-calculate
(lambda (x _y)
(vector buffer-or-id
(eval-when-compile
(logior xcb:ConfigWindow:X
xcb:ConfigWindow:Width))
(- x win-x) 0 (- (+ root-x width) x) 0)))))
;; Select events and change cursor (should always succeed)
(xcb:+request-unchecked+reply exwm--connection
(make-instance 'xcb:GrabPointer
:owner-events 0 :grab-window container-or-id
:event-mask (eval-when-compile
(logior xcb:EventMask:ButtonRelease
xcb:EventMask:ButtonMotion))
:pointer-mode xcb:GrabMode:Async
:keyboard-mode xcb:GrabMode:Async
:confine-to xcb:Window:None
:cursor cursor
:time xcb:Time:CurrentTime)))))))
(defun exwm-floating--stop-moveresize (&rest _args)
"Stop move/resize."
(exwm--log)
(xcb:+request exwm--connection
(make-instance 'xcb:UngrabPointer :time xcb:Time:CurrentTime))
(when exwm-floating--moveresize-calculate
(let (result buffer-or-id outer-id container-id)
(setq result (funcall exwm-floating--moveresize-calculate 0 0)
buffer-or-id (aref result 0))
(when (bufferp buffer-or-id)
(with-current-buffer buffer-or-id
(setq outer-id (frame-parameter exwm--floating-frame 'exwm-outer-id)
container-id (frame-parameter exwm--floating-frame
'exwm-container))
(with-slots (x y width height border-width)
(xcb:+request-unchecked+reply exwm--connection
(make-instance 'xcb:GetGeometry
:drawable container-id))
;; Notify Emacs frame about this the position change.
(xcb:+request exwm--connection
(make-instance 'xcb:SendEvent
:propagate 0
:destination outer-id
:event-mask xcb:EventMask:StructureNotify
:event
(xcb:marshal
(make-instance 'xcb:ConfigureNotify
:event outer-id
:window outer-id
:above-sibling xcb:Window:None
:x (+ x border-width)
:y (+ y border-width)
:width width
:height height
:border-width 0
:override-redirect 0)
exwm--connection)))
(xcb:flush exwm--connection))
(exwm-layout--show exwm--id
(frame-root-window exwm--floating-frame)))))
(setq exwm-floating--moveresize-calculate nil)))
(defun exwm-floating--do-moveresize (data _synthetic)
"Perform move/resize."
(when exwm-floating--moveresize-calculate
(let* ((obj (make-instance 'xcb:MotionNotify))
result value-mask x y width height buffer-or-id container-or-id)
(xcb:unmarshal obj data)
(setq result (funcall exwm-floating--moveresize-calculate
(slot-value obj 'root-x) (slot-value obj 'root-y))
buffer-or-id (aref result 0)
value-mask (aref result 1)
x (aref result 2)
y (aref result 3)
width (max 1 (aref result 4))
height (max 1 (aref result 5)))
(if (not (bufferp buffer-or-id))
;; Unmanaged.
(setq container-or-id buffer-or-id)
;; Managed.
(setq container-or-id
(with-current-buffer buffer-or-id
(frame-parameter exwm--floating-frame 'exwm-container))
x (- x exwm-floating-border-width)
;; Use `frame-outer-height' in the future.
y (- y exwm-floating-border-width
exwm-workspace--window-y-offset)
height (+ height exwm-workspace--window-y-offset)))
(xcb:+request exwm--connection
(make-instance 'xcb:ConfigureWindow
:window container-or-id
:value-mask (aref result 1)
:x x
:y y
:width width
:height height))
(when (bufferp buffer-or-id)
;; Managed.
(setq value-mask (logand value-mask (logior xcb:ConfigWindow:Width
xcb:ConfigWindow:Height)))
(when (/= 0 value-mask)
(with-current-buffer buffer-or-id
(xcb:+request exwm--connection
(make-instance 'xcb:ConfigureWindow
:window (frame-parameter exwm--floating-frame
'exwm-outer-id)
:value-mask value-mask
:width width
:height height)))))
(xcb:flush exwm--connection))))
(defun exwm-floating-move (&optional delta-x delta-y)
"Move a floating window right by DELTA-X pixels and down by DELTA-Y pixels.
Both DELTA-X and DELTA-Y default to 1. This command should be bound locally."
(exwm--log "delta-x: %s, delta-y: %s" delta-x delta-y)
(unless (and (derived-mode-p 'exwm-mode) exwm--floating-frame)
(user-error "[EXWM] `exwm-floating-move' is only for floating X windows"))
(unless delta-x (setq delta-x 1))
(unless delta-y (setq delta-y 1))
(unless (and (= 0 delta-x) (= 0 delta-y))
(let* ((floating-container (frame-parameter exwm--floating-frame
'exwm-container))
(geometry (xcb:+request-unchecked+reply exwm--connection
(make-instance 'xcb:GetGeometry
:drawable floating-container)))
(edges (window-inside-absolute-pixel-edges)))
(with-slots (x y) geometry
(exwm--set-geometry floating-container
(+ x delta-x) (+ y delta-y) nil nil))
(exwm--set-geometry exwm--id
(+ (pop edges) delta-x)
(+ (pop edges) delta-y)
nil nil))
(xcb:flush exwm--connection)))
(defun exwm-floating--init ()
"Initialize floating module."
(exwm--log)
;; Initialize cursors for moving/resizing a window
(xcb:cursor:init exwm--connection)
(setq exwm-floating--cursor-move
(xcb:cursor:load-cursor exwm--connection "fleur")
exwm-floating--cursor-top-left
(xcb:cursor:load-cursor exwm--connection "top_left_corner")
exwm-floating--cursor-top
(xcb:cursor:load-cursor exwm--connection "top_side")
exwm-floating--cursor-top-right
(xcb:cursor:load-cursor exwm--connection "top_right_corner")
exwm-floating--cursor-right
(xcb:cursor:load-cursor exwm--connection "right_side")
exwm-floating--cursor-bottom-right
(xcb:cursor:load-cursor exwm--connection "bottom_right_corner")
exwm-floating--cursor-bottom
(xcb:cursor:load-cursor exwm--connection "bottom_side")
exwm-floating--cursor-bottom-left
(xcb:cursor:load-cursor exwm--connection "bottom_left_corner")
exwm-floating--cursor-left
(xcb:cursor:load-cursor exwm--connection "left_side")))
(defun exwm-floating--exit ()
"Exit the floating module."
(exwm--log))
(provide 'exwm-floating)
;;; exwm-floating.el ends here

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@ -1,618 +0,0 @@
;;; exwm-layout.el --- Layout Module for EXWM -*- lexical-binding: t -*-
;; Copyright (C) 2015-2020 Free Software Foundation, Inc.
;; Author: Chris Feng <chris.w.feng@gmail.com>
;; This file is part of GNU Emacs.
;; GNU Emacs 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 3 of the License, or
;; (at your option) any later version.
;; GNU Emacs 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 GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
;;; Commentary:
;; This module is responsible for keeping X client window properly displayed.
;;; Code:
(require 'exwm-core)
(defgroup exwm-layout nil
"Layout."
:version "25.3"
:group 'exwm)
(defcustom exwm-layout-auto-iconify t
"Non-nil to automatically iconify unused X windows when possible."
:type 'boolean)
(defcustom exwm-layout-show-all-buffers nil
"Non-nil to allow switching to buffers on other workspaces."
:type 'boolean)
(defconst exwm-layout--floating-hidden-position -101
"Where to place hidden floating X windows.")
(defvar exwm-layout--other-buffer-exclude-buffers nil
"List of buffers that should not be selected by `other-buffer'.")
(defvar exwm-layout--other-buffer-exclude-exwm-mode-buffers nil
"When non-nil, prevent EXWM buffers from being selected by `other-buffer'.")
(defvar exwm-layout--timer nil "Timer used to track echo area changes.")
(defvar exwm-workspace--current)
(defvar exwm-workspace--frame-y-offset)
(declare-function exwm-input--release-keyboard "exwm-input.el")
(declare-function exwm-input--grab-keyboard "exwm-input.el")
(declare-function exwm-input-grab-keyboard "exwm-input.el")
(declare-function exwm-workspace--active-p "exwm-workspace.el" (frame))
(declare-function exwm-workspace--client-p "exwm-workspace.el"
(&optional frame))
(declare-function exwm-workspace--minibuffer-own-frame-p "exwm-workspace.el")
(declare-function exwm-workspace--workspace-p "exwm-workspace.el"
(workspace))
(declare-function exwm-workspace-move-window "exwm-workspace.el"
(frame-or-index &optional id))
(defun exwm-layout--set-state (id state)
"Set WM_STATE."
(exwm--log "id=#x%x" id)
(xcb:+request exwm--connection
(make-instance 'xcb:icccm:set-WM_STATE
:window id :state state :icon xcb:Window:None))
(with-current-buffer (exwm--id->buffer id)
(setq exwm-state state)))
(defun exwm-layout--iconic-state-p (&optional id)
(= xcb:icccm:WM_STATE:IconicState
(if id
(buffer-local-value 'exwm-state (exwm--id->buffer id))
exwm-state)))
(defun exwm-layout--set-ewmh-state (xwin)
"Set _NET_WM_STATE."
(with-current-buffer (exwm--id->buffer xwin)
(xcb:+request exwm--connection
(make-instance 'xcb:ewmh:set-_NET_WM_STATE
:window exwm--id
:data exwm--ewmh-state))))
(defun exwm-layout--fullscreen-p ()
(when (derived-mode-p 'exwm-mode)
(memq xcb:Atom:_NET_WM_STATE_FULLSCREEN exwm--ewmh-state)))
(defun exwm-layout--auto-iconify ()
(when (and exwm-layout-auto-iconify
(not exwm-transient-for))
(let ((xwin exwm--id)
(state exwm-state))
(dolist (pair exwm--id-buffer-alist)
(with-current-buffer (cdr pair)
(when (and exwm--floating-frame
(eq exwm-transient-for xwin)
(not (eq exwm-state state)))
(if (eq state xcb:icccm:WM_STATE:NormalState)
(exwm-layout--refresh-floating exwm--floating-frame)
(exwm-layout--hide exwm--id))))))))
(defun exwm-layout--show (id &optional window)
"Show window ID exactly fit in the Emacs window WINDOW."
(exwm--log "Show #x%x in %s" id window)
(let* ((edges (window-inside-absolute-pixel-edges window))
(x (pop edges))
(y (pop edges))
(width (- (pop edges) x))
(height (- (pop edges) y))
frame-x frame-y frame-width frame-height)
(with-current-buffer (exwm--id->buffer id)
(when exwm--floating-frame
(setq frame-width (frame-pixel-width exwm--floating-frame)
frame-height (+ (frame-pixel-height exwm--floating-frame)
;; Use `frame-outer-height' in the future.
exwm-workspace--frame-y-offset))
(when exwm--floating-frame-position
(setq frame-x (elt exwm--floating-frame-position 0)
frame-y (elt exwm--floating-frame-position 1)
x (+ x frame-x (- exwm-layout--floating-hidden-position))
y (+ y frame-y (- exwm-layout--floating-hidden-position)))
(setq exwm--floating-frame-position nil))
(exwm--set-geometry (frame-parameter exwm--floating-frame
'exwm-container)
frame-x frame-y frame-width frame-height))
(when (exwm-layout--fullscreen-p)
(with-slots ((x* x)
(y* y)
(width* width)
(height* height))
(exwm-workspace--get-geometry exwm--frame)
(setq x x*
y y*
width width*
height height*)))
(exwm--set-geometry id x y width height)
(xcb:+request exwm--connection (make-instance 'xcb:MapWindow :window id))
(exwm-layout--set-state id xcb:icccm:WM_STATE:NormalState)
(setq exwm--ewmh-state
(delq xcb:Atom:_NET_WM_STATE_HIDDEN exwm--ewmh-state))
(exwm-layout--set-ewmh-state id)
(exwm-layout--auto-iconify)))
(xcb:flush exwm--connection))
(defun exwm-layout--hide (id)
"Hide window ID."
(with-current-buffer (exwm--id->buffer id)
(unless (or (exwm-layout--iconic-state-p)
(and exwm--floating-frame
(eq 4294967295. exwm--desktop)))
(exwm--log "Hide #x%x" id)
(when exwm--floating-frame
(let* ((container (frame-parameter exwm--floating-frame
'exwm-container))
(geometry (xcb:+request-unchecked+reply exwm--connection
(make-instance 'xcb:GetGeometry
:drawable container))))
(setq exwm--floating-frame-position
(vector (slot-value geometry 'x) (slot-value geometry 'y)))
(exwm--set-geometry container exwm-layout--floating-hidden-position
exwm-layout--floating-hidden-position
1
1)))
(xcb:+request exwm--connection
(make-instance 'xcb:ChangeWindowAttributes
:window id :value-mask xcb:CW:EventMask
:event-mask xcb:EventMask:NoEvent))
(xcb:+request exwm--connection
(make-instance 'xcb:UnmapWindow :window id))
(xcb:+request exwm--connection
(make-instance 'xcb:ChangeWindowAttributes
:window id :value-mask xcb:CW:EventMask
:event-mask (exwm--get-client-event-mask)))
(exwm-layout--set-state id xcb:icccm:WM_STATE:IconicState)
(cl-pushnew xcb:Atom:_NET_WM_STATE_HIDDEN exwm--ewmh-state)
(exwm-layout--set-ewmh-state id)
(exwm-layout--auto-iconify)
(xcb:flush exwm--connection))))
;;;###autoload
(cl-defun exwm-layout-set-fullscreen (&optional id)
"Make window ID fullscreen."
(interactive)
(exwm--log "id=#x%x" (or id 0))
(unless (and (or id (derived-mode-p 'exwm-mode))
(not (exwm-layout--fullscreen-p)))
(cl-return-from exwm-layout-set-fullscreen))
(with-current-buffer (if id (exwm--id->buffer id) (window-buffer))
;; Expand the X window to fill the whole screen.
(with-slots (x y width height) (exwm-workspace--get-geometry exwm--frame)
(exwm--set-geometry exwm--id x y width height))
;; Raise the X window.
(xcb:+request exwm--connection
(make-instance 'xcb:ConfigureWindow
:window exwm--id
:value-mask (logior xcb:ConfigWindow:BorderWidth
xcb:ConfigWindow:StackMode)
:border-width 0
:stack-mode xcb:StackMode:Above))
(cl-pushnew xcb:Atom:_NET_WM_STATE_FULLSCREEN exwm--ewmh-state)
(exwm-layout--set-ewmh-state id)
(xcb:flush exwm--connection)
(set-window-dedicated-p (get-buffer-window) t)
(exwm-input--release-keyboard exwm--id)))
;;;###autoload
(cl-defun exwm-layout-unset-fullscreen (&optional id)
"Restore window from fullscreen state."
(interactive)
(exwm--log "id=#x%x" (or id 0))
(unless (and (or id (derived-mode-p 'exwm-mode))
(exwm-layout--fullscreen-p))
(cl-return-from exwm-layout-unset-fullscreen))
(with-current-buffer (if id (exwm--id->buffer id) (window-buffer))
(setq exwm--ewmh-state
(delq xcb:Atom:_NET_WM_STATE_FULLSCREEN exwm--ewmh-state))
(if exwm--floating-frame
(exwm-layout--show exwm--id (frame-root-window exwm--floating-frame))
(xcb:+request exwm--connection
(make-instance 'xcb:ConfigureWindow
:window exwm--id
:value-mask (logior xcb:ConfigWindow:Sibling
xcb:ConfigWindow:StackMode)
:sibling exwm--guide-window
:stack-mode xcb:StackMode:Above))
(let ((window (get-buffer-window nil t)))
(when window
(exwm-layout--show exwm--id window))))
(exwm-layout--set-ewmh-state id)
(xcb:flush exwm--connection)
(set-window-dedicated-p (get-buffer-window) nil)
(when (eq 'line-mode exwm--selected-input-mode)
(exwm-input--grab-keyboard exwm--id))))
;;;###autoload
(cl-defun exwm-layout-toggle-fullscreen (&optional id)
"Toggle fullscreen mode."
(interactive (list (exwm--buffer->id (window-buffer))))
(exwm--log "id=#x%x" (or id 0))
(unless (or id (derived-mode-p 'exwm-mode))
(cl-return-from exwm-layout-toggle-fullscreen))
(when id
(with-current-buffer (exwm--id->buffer id)
(if (exwm-layout--fullscreen-p)
(exwm-layout-unset-fullscreen id)
(exwm-layout-set-fullscreen id)))))
(defun exwm-layout--other-buffer-predicate (buffer)
"Return non-nil when the BUFFER may be displayed in selected frame.
Prevents EXWM-mode buffers already being displayed on some other window from
being selected.
Should be set as `buffer-predicate' frame parameter for all
frames. Used by `other-buffer'.
When variable `exwm-layout--other-buffer-exclude-exwm-mode-buffers'
is t EXWM buffers are never selected by `other-buffer'.
When variable `exwm-layout--other-buffer-exclude-buffers' is a
list of buffers, EXWM buffers belonging to that list are never
selected by `other-buffer'."
(or (not (with-current-buffer buffer (derived-mode-p 'exwm-mode)))
(and (not exwm-layout--other-buffer-exclude-exwm-mode-buffers)
(not (memq buffer exwm-layout--other-buffer-exclude-buffers))
;; Do not select if already shown in some window.
(not (get-buffer-window buffer t)))))
(defun exwm-layout--set-client-list-stacking ()
"Set _NET_CLIENT_LIST_STACKING."
(exwm--log)
(let (id clients-floating clients clients-iconic clients-other)
(dolist (pair exwm--id-buffer-alist)
(setq id (car pair))
(with-current-buffer (cdr pair)
(if (eq exwm--frame exwm-workspace--current)
(if exwm--floating-frame
;; A floating X window on the current workspace.
(setq clients-floating (cons id clients-floating))
(if (get-buffer-window (cdr pair) exwm-workspace--current)
;; A normal tilling X window on the current workspace.
(setq clients (cons id clients))
;; An iconic tilling X window on the current workspace.
(setq clients-iconic (cons id clients-iconic))))
;; X window on other workspaces.
(setq clients-other (cons id clients-other)))))
(xcb:+request exwm--connection
(make-instance 'xcb:ewmh:set-_NET_CLIENT_LIST_STACKING
:window exwm--root
:data (vconcat (append clients-other clients-iconic
clients clients-floating))))))
(defun exwm-layout--refresh (&optional frame)
"Refresh layout."
;; `window-size-change-functions' sets this argument while
;; `window-configuration-change-hook' makes the frame selected.
(unless frame
(setq frame (selected-frame)))
(exwm--log "frame=%s" frame)
(if (not (exwm-workspace--workspace-p frame))
(if (frame-parameter frame 'exwm-outer-id)
(exwm-layout--refresh-floating frame)
(exwm-layout--refresh-other frame))
(exwm-layout--refresh-workspace frame)))
(defun exwm-layout--refresh-floating (frame)
"Refresh floating frame FRAME."
(exwm--log "Refresh floating %s" frame)
(let ((window (frame-first-window frame)))
(with-current-buffer (window-buffer window)
(when (and (derived-mode-p 'exwm-mode)
;; It may be a buffer waiting to be killed.
(exwm--id->buffer exwm--id))
(exwm--log "Refresh floating window #x%x" exwm--id)
(if (exwm-workspace--active-p exwm--frame)
(exwm-layout--show exwm--id window)
(exwm-layout--hide exwm--id))))))
(defun exwm-layout--refresh-other (frame)
"Refresh client or nox frame FRAME."
;; Other frames (e.g. terminal/graphical frame of emacsclient)
;; We shall bury all `exwm-mode' buffers in this case
(exwm--log "Refresh other %s" frame)
(let ((windows (window-list frame 'nomini)) ;exclude minibuffer
(exwm-layout--other-buffer-exclude-exwm-mode-buffers t))
(dolist (window windows)
(with-current-buffer (window-buffer window)
(when (derived-mode-p 'exwm-mode)
(if (window-prev-buffers window)
(switch-to-prev-buffer window)
(switch-to-next-buffer window)))))))
(defun exwm-layout--refresh-workspace (frame)
"Refresh workspace frame FRAME."
(exwm--log "Refresh workspace %s" frame)
;; Workspaces other than the active one can also be refreshed (RandR)
(let (covered-buffers ;EXWM-buffers covered by a new X window.
vacated-windows) ;Windows previously displaying EXWM-buffers.
(dolist (pair exwm--id-buffer-alist)
(with-current-buffer (cdr pair)
(when (and (not exwm--floating-frame) ;exclude floating X windows
(or exwm-layout-show-all-buffers
;; Exclude X windows on other workspaces
(eq frame exwm--frame)))
(let (;; List of windows in current frame displaying the `exwm-mode'
;; buffers.
(windows (get-buffer-window-list (current-buffer) 'nomini
frame)))
(if (not windows)
(when (eq frame exwm--frame)
;; Hide it if it was being shown in this workspace.
(exwm-layout--hide exwm--id))
(let ((window (car windows)))
(if (eq frame exwm--frame)
;; Show it if `frame' is active, hide otherwise.
(if (exwm-workspace--active-p frame)
(exwm-layout--show exwm--id window)
(exwm-layout--hide exwm--id))
;; It was last shown in other workspace; move it here.
(exwm-workspace-move-window frame exwm--id))
;; Vacate any other windows (in any workspace) showing this
;; `exwm-mode' buffer.
(setq vacated-windows
(append vacated-windows (remove
window
(get-buffer-window-list
(current-buffer) 'nomini t))))
;; Note any `exwm-mode' buffer is being covered by another
;; `exwm-mode' buffer. We want to avoid that `exwm-mode'
;; buffer to be reappear in any of the vacated windows.
(let ((prev-buffer (car-safe
(car-safe (window-prev-buffers window)))))
(and
prev-buffer
(with-current-buffer prev-buffer
(derived-mode-p 'exwm-mode))
(push prev-buffer covered-buffers)))))))))
;; Set some sensible buffer to vacated windows.
(let ((exwm-layout--other-buffer-exclude-buffers covered-buffers))
(dolist (window vacated-windows)
(if (window-prev-buffers window)
(switch-to-prev-buffer window)
(switch-to-next-buffer window))))
;; Make sure windows floating / on other workspaces are excluded
(let ((exwm-layout--other-buffer-exclude-exwm-mode-buffers t))
(dolist (window (window-list frame 'nomini))
(with-current-buffer (window-buffer window)
(when (and (derived-mode-p 'exwm-mode)
(or exwm--floating-frame (not (eq frame exwm--frame))))
(if (window-prev-buffers window)
(switch-to-prev-buffer window)
(switch-to-next-buffer window))))))
(exwm-layout--set-client-list-stacking)
(xcb:flush exwm--connection)))
(defun exwm-layout--on-minibuffer-setup ()
"Refresh layout when minibuffer grows."
(exwm--log)
(unless (exwm-workspace--client-p)
(exwm--defer 0 (lambda ()
(when (< 1 (window-height (minibuffer-window)))
(exwm-layout--refresh))))))
(defun exwm-layout--on-echo-area-change (&optional dirty)
"Run when message arrives or in `echo-area-clear-hook' to refresh layout."
(when (and (current-message)
(not (exwm-workspace--client-p))
(or (cl-position ?\n (current-message))
(> (length (current-message))
(frame-width exwm-workspace--current))))
(exwm--log)
(if dirty
(exwm-layout--refresh)
(exwm--defer 0 #'exwm-layout--refresh))))
;;;###autoload
(defun exwm-layout-enlarge-window (delta &optional horizontal)
"Make the selected window DELTA pixels taller.
If no argument is given, make the selected window one pixel taller. If the
optional argument HORIZONTAL is non-nil, make selected window DELTA pixels
wider. If DELTA is negative, shrink selected window by -DELTA pixels.
Normal hints are checked and regarded if the selected window is displaying an
`exwm-mode' buffer. However, this may violate the normal hints set on other X
windows."
(interactive "p")
(exwm--log)
(cond
((zerop delta)) ;no operation
((window-minibuffer-p)) ;avoid resize minibuffer-window
((not (and (derived-mode-p 'exwm-mode) exwm--floating-frame))
;; Resize on tiling layout
(unless (= 0 (window-resizable nil delta horizontal nil t)) ;not resizable
(let ((window-resize-pixelwise t))
(window-resize nil delta horizontal nil t))))
;; Resize on floating layout
(exwm--fixed-size) ;fixed size
(horizontal
(let* ((width (frame-pixel-width))
(edges (window-inside-pixel-edges))
(inner-width (- (elt edges 2) (elt edges 0)))
(margin (- width inner-width)))
(if (> delta 0)
(if (not exwm--normal-hints-max-width)
(cl-incf width delta)
(if (>= inner-width exwm--normal-hints-max-width)
(setq width nil)
(setq width (min (+ exwm--normal-hints-max-width margin)
(+ width delta)))))
(if (not exwm--normal-hints-min-width)
(cl-incf width delta)
(if (<= inner-width exwm--normal-hints-min-width)
(setq width nil)
(setq width (max (+ exwm--normal-hints-min-width margin)
(+ width delta))))))
(when (and width (> width 0))
(setf (slot-value exwm--geometry 'width) width)
(xcb:+request exwm--connection
(make-instance 'xcb:ConfigureWindow
:window (frame-parameter exwm--floating-frame
'exwm-outer-id)
:value-mask xcb:ConfigWindow:Width
:width width))
(xcb:+request exwm--connection
(make-instance 'xcb:ConfigureWindow
:window (frame-parameter exwm--floating-frame
'exwm-container)
:value-mask xcb:ConfigWindow:Width
:width width))
(xcb:flush exwm--connection))))
(t
(let* ((height (+ (frame-pixel-height) exwm-workspace--frame-y-offset))
(edges (window-inside-pixel-edges))
(inner-height (- (elt edges 3) (elt edges 1)))
(margin (- height inner-height)))
(if (> delta 0)
(if (not exwm--normal-hints-max-height)
(cl-incf height delta)
(if (>= inner-height exwm--normal-hints-max-height)
(setq height nil)
(setq height (min (+ exwm--normal-hints-max-height margin)
(+ height delta)))))
(if (not exwm--normal-hints-min-height)
(cl-incf height delta)
(if (<= inner-height exwm--normal-hints-min-height)
(setq height nil)
(setq height (max (+ exwm--normal-hints-min-height margin)
(+ height delta))))))
(when (and height (> height 0))
(setf (slot-value exwm--geometry 'height) height)
(xcb:+request exwm--connection
(make-instance 'xcb:ConfigureWindow
:window (frame-parameter exwm--floating-frame
'exwm-outer-id)
:value-mask xcb:ConfigWindow:Height
:height height))
(xcb:+request exwm--connection
(make-instance 'xcb:ConfigureWindow
:window (frame-parameter exwm--floating-frame
'exwm-container)
:value-mask xcb:ConfigWindow:Height
:height height))
(xcb:flush exwm--connection))))))
;;;###autoload
(defun exwm-layout-enlarge-window-horizontally (delta)
"Make the selected window DELTA pixels wider.
See also `exwm-layout-enlarge-window'."
(interactive "p")
(exwm--log "%s" delta)
(exwm-layout-enlarge-window delta t))
;;;###autoload
(defun exwm-layout-shrink-window (delta)
"Make the selected window DELTA pixels lower.
See also `exwm-layout-enlarge-window'."
(interactive "p")
(exwm--log "%s" delta)
(exwm-layout-enlarge-window (- delta)))
;;;###autoload
(defun exwm-layout-shrink-window-horizontally (delta)
"Make the selected window DELTA pixels narrower.
See also `exwm-layout-enlarge-window'."
(interactive "p")
(exwm--log "%s" delta)
(exwm-layout-enlarge-window (- delta) t))
;;;###autoload
(defun exwm-layout-hide-mode-line ()
"Hide mode-line."
(interactive)
(exwm--log)
(when (and (derived-mode-p 'exwm-mode) mode-line-format)
(let (mode-line-height)
(when exwm--floating-frame
(setq mode-line-height (window-mode-line-height
(frame-root-window exwm--floating-frame))))
(setq exwm--mode-line-format mode-line-format
mode-line-format nil)
(if (not exwm--floating-frame)
(exwm-layout--show exwm--id)
(set-frame-height exwm--floating-frame
(- (frame-pixel-height exwm--floating-frame)
mode-line-height)
nil t)))))
;;;###autoload
(defun exwm-layout-show-mode-line ()
"Show mode-line."
(interactive)
(exwm--log)
(when (and (derived-mode-p 'exwm-mode) (not mode-line-format))
(setq mode-line-format exwm--mode-line-format
exwm--mode-line-format nil)
(if (not exwm--floating-frame)
(exwm-layout--show exwm--id)
(set-frame-height exwm--floating-frame
(+ (frame-pixel-height exwm--floating-frame)
(window-mode-line-height (frame-root-window
exwm--floating-frame)))
nil t)
(call-interactively #'exwm-input-grab-keyboard))
(force-mode-line-update)))
;;;###autoload
(defun exwm-layout-toggle-mode-line ()
"Toggle the display of mode-line."
(interactive)
(exwm--log)
(when (derived-mode-p 'exwm-mode)
(if mode-line-format
(exwm-layout-hide-mode-line)
(exwm-layout-show-mode-line))))
(defun exwm-layout--init ()
"Initialize layout module."
;; Auto refresh layout
(exwm--log)
(add-hook 'window-configuration-change-hook #'exwm-layout--refresh)
;; The behavior of `window-configuration-change-hook' will be changed.
(when (fboundp 'window-pixel-width-before-size-change)
(add-hook 'window-size-change-functions #'exwm-layout--refresh))
(unless (exwm-workspace--minibuffer-own-frame-p)
;; Refresh when minibuffer grows
(add-hook 'minibuffer-setup-hook #'exwm-layout--on-minibuffer-setup t)
(setq exwm-layout--timer
(run-with-idle-timer 0 t #'exwm-layout--on-echo-area-change t))
(add-hook 'echo-area-clear-hook #'exwm-layout--on-echo-area-change)))
(defun exwm-layout--exit ()
"Exit the layout module."
(exwm--log)
(remove-hook 'window-configuration-change-hook #'exwm-layout--refresh)
(when (fboundp 'window-pixel-width-before-size-change)
(remove-hook 'window-size-change-functions #'exwm-layout--refresh))
(remove-hook 'minibuffer-setup-hook #'exwm-layout--on-minibuffer-setup)
(when exwm-layout--timer
(cancel-timer exwm-layout--timer)
(setq exwm-layout--timer nil))
(remove-hook 'echo-area-clear-hook #'exwm-layout--on-echo-area-change))
(provide 'exwm-layout)
;;; exwm-layout.el ends here

Binary file not shown.

View File

@ -1,805 +0,0 @@
;;; exwm-manage.el --- Window Management Module for -*- lexical-binding: t -*-
;;; EXWM
;; Copyright (C) 2015-2020 Free Software Foundation, Inc.
;; Author: Chris Feng <chris.w.feng@gmail.com>
;; This file is part of GNU Emacs.
;; GNU Emacs 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 3 of the License, or
;; (at your option) any later version.
;; GNU Emacs 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 GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
;;; Commentary:
;; This is the fundamental module of EXWM that deals with window management.
;;; Code:
(require 'exwm-core)
(defgroup exwm-manage nil
"Manage."
:version "25.3"
:group 'exwm)
(defcustom exwm-manage-finish-hook nil
"Normal hook run after a window is just managed, in the context of the
corresponding buffer."
:type 'hook)
(defcustom exwm-manage-force-tiling nil
"Non-nil to force managing all X windows in tiling layout.
You can still make the X windows floating afterwards."
:type 'boolean)
(defcustom exwm-manage-ping-timeout 3
"Seconds to wait before killing a client."
:type 'integer)
(defcustom exwm-manage-configurations nil
"Per-application configurations.
Configuration options allow to override various default behaviors of EXWM
and only take effect when they are present. Note for certain options
specifying nil is not exactly the same as leaving them out. Currently
possible choices:
* floating: Force floating (non-nil) or tiling (nil) on startup.
* x/y/width/height: Override the initial geometry (floating X window only).
* border-width: Override the border width (only visible when floating).
* fullscreen: Force full screen (non-nil) on startup.
* floating-mode-line: `mode-line-format' used when floating.
* tiling-mode-line: `mode-line-format' used when tiling.
* floating-header-line: `header-line-format' used when floating.
* tiling-header-line: `header-line-format' used when tiling.
* char-mode: Force char-mode (non-nil) on startup.
* prefix-keys: `exwm-input-prefix-keys' local to this X window.
* simulation-keys: `exwm-input-simulation-keys' local to this X window.
* workspace: The initial workspace.
* managed: Force to manage (non-nil) or not manage (nil) the X window.
For each X window managed for the first time, matching criteria (sexps) are
evaluated sequentially and the first configuration with a non-nil matching
criterion would be applied. Apart from generic forms, one would typically
want to match against EXWM internal variables such as `exwm-title',
`exwm-class-name' and `exwm-instance-name'."
:type '(alist :key-type (sexp :tag "Matching criterion" nil)
:value-type
(plist :tag "Configurations"
:options
(((const :tag "Floating" floating) boolean)
((const :tag "X" x) number)
((const :tag "Y" y) number)
((const :tag "Width" width) number)
((const :tag "Height" height) number)
((const :tag "Border width" border-width) integer)
((const :tag "Fullscreen" fullscreen) boolean)
((const :tag "Floating mode-line" floating-mode-line)
sexp)
((const :tag "Tiling mode-line" tiling-mode-line) sexp)
((const :tag "Floating header-line"
floating-header-line)
sexp)
((const :tag "Tiling header-line" tiling-header-line)
sexp)
((const :tag "Char-mode" char-mode) boolean)
((const :tag "Prefix keys" prefix-keys)
(repeat key-sequence))
((const :tag "Simulation keys" simulation-keys)
(alist :key-type (key-sequence :tag "From")
:value-type (key-sequence :tag "To")))
((const :tag "Workspace" workspace) integer)
((const :tag "Managed" managed) boolean)
;; For forward compatibility.
((other) sexp))))
;; TODO: This is admittedly ugly. We'd be better off with an event type.
:get (lambda (symbol)
(mapcar (lambda (pair)
(let* ((match (car pair))
(config (cdr pair))
(prefix-keys (plist-get config 'prefix-keys)))
(when prefix-keys
(setq config (copy-tree config)
config (plist-put config 'prefix-keys
(mapcar (lambda (i)
(if (sequencep i)
i
(vector i)))
prefix-keys))))
(cons match config)))
(default-value symbol)))
:set (lambda (symbol value)
(set symbol
(mapcar (lambda (pair)
(let* ((match (car pair))
(config (cdr pair))
(prefix-keys (plist-get config 'prefix-keys)))
(when prefix-keys
(setq config (copy-tree config)
config (plist-put config 'prefix-keys
(mapcar (lambda (i)
(if (sequencep i)
(aref i 0)
i))
prefix-keys))))
(cons match config)))
value))))
;; FIXME: Make the following values as small as possible.
(defconst exwm-manage--height-delta-min 5)
(defconst exwm-manage--width-delta-min 5)
;; The _MOTIF_WM_HINTS atom (see <Xm/MwmUtil.h> for more details)
;; It's currently only used in 'exwm-manage' module
(defvar exwm-manage--_MOTIF_WM_HINTS nil "_MOTIF_WM_HINTS atom.")
(defvar exwm-manage--desktop nil "The desktop X window.")
(defvar exwm-manage--frame-outer-id-list nil
"List of window-outer-id's of all frames.")
(defvar exwm-manage--ping-lock nil
"Non-nil indicates EXWM is pinging a window.")
(defvar exwm-input--skip-buffer-list-update)
(defvar exwm-input-prefix-keys)
(defvar exwm-workspace--current)
(defvar exwm-workspace--id-struts-alist)
(defvar exwm-workspace--list)
(defvar exwm-workspace--switch-history-outdated)
(defvar exwm-workspace--workareas)
(defvar exwm-workspace-current-index)
(declare-function exwm--update-class "exwm.el" (id &optional force))
(declare-function exwm--update-hints "exwm.el" (id &optional force))
(declare-function exwm--update-normal-hints "exwm.el" (id &optional force))
(declare-function exwm--update-protocols "exwm.el" (id &optional force))
(declare-function exwm--update-struts "exwm.el" (id))
(declare-function exwm--update-title "exwm.el" (id))
(declare-function exwm--update-transient-for "exwm.el" (id &optional force))
(declare-function exwm--update-desktop "exwm.el" (id &optional force))
(declare-function exwm--update-window-type "exwm.el" (id &optional force))
(declare-function exwm-floating--set-floating "exwm-floating.el" (id))
(declare-function exwm-floating--unset-floating "exwm-floating.el" (id))
(declare-function exwm-input-grab-keyboard "exwm-input.el")
(declare-function exwm-input-set-local-simulation-keys "exwm-input.el")
(declare-function exwm-layout--fullscreen-p "exwm-layout.el" ())
(declare-function exwm-layout--iconic-state-p "exwm-layout.el" (&optional id))
(declare-function exwm-workspace--position "exwm-workspace.el" (frame))
(declare-function exwm-workspace--set-fullscreen "exwm-workspace.el" (frame))
(declare-function exwm-workspace--update-struts "exwm-workspace.el" ())
(declare-function exwm-workspace--update-workareas "exwm-workspace.el" ())
(defun exwm-manage--update-geometry (id &optional force)
"Update window geometry."
(exwm--log "id=#x%x" id)
(with-current-buffer (exwm--id->buffer id)
(unless (and exwm--geometry (not force))
(let ((reply (xcb:+request-unchecked+reply exwm--connection
(make-instance 'xcb:GetGeometry :drawable id))))
(setq exwm--geometry
(or reply
;; Provide a reasonable fallback value.
(make-instance 'xcb:RECTANGLE
:x 0
:y 0
:width (/ (x-display-pixel-width) 2)
:height (/ (x-display-pixel-height) 2))))))))
(defun exwm-manage--update-ewmh-state (id)
"Update _NET_WM_STATE."
(exwm--log "id=#x%x" id)
(with-current-buffer (exwm--id->buffer id)
(unless exwm--ewmh-state
(let ((reply (xcb:+request-unchecked+reply exwm--connection
(make-instance 'xcb:ewmh:get-_NET_WM_STATE
:window id))))
(when reply
(setq exwm--ewmh-state (append (slot-value reply 'value) nil)))))))
(defun exwm-manage--update-mwm-hints (id &optional force)
"Update _MOTIF_WM_HINTS."
(exwm--log "id=#x%x" id)
(with-current-buffer (exwm--id->buffer id)
(unless (and (not exwm--mwm-hints-decorations) (not force))
(let ((reply (xcb:+request-unchecked+reply exwm--connection
(make-instance 'xcb:icccm:-GetProperty
:window id
:property exwm-manage--_MOTIF_WM_HINTS
:type exwm-manage--_MOTIF_WM_HINTS
:long-length 5))))
(when reply
;; Check MotifWmHints.decorations.
(with-slots (value) reply
(setq value (append value nil))
(when (and value
;; See <Xm/MwmUtil.h> for fields definitions.
(/= 0 (logand
(elt value 0) ;MotifWmHints.flags
2)) ;MWM_HINTS_DECORATIONS
(= 0
(elt value 2))) ;MotifWmHints.decorations
(setq exwm--mwm-hints-decorations nil))))))))
(defun exwm-manage--set-client-list ()
"Set _NET_CLIENT_LIST."
(exwm--log)
(xcb:+request exwm--connection
(make-instance 'xcb:ewmh:set-_NET_CLIENT_LIST
:window exwm--root
:data (vconcat (mapcar #'car exwm--id-buffer-alist)))))
(cl-defun exwm-manage--get-configurations ()
"Retrieve configurations for this buffer."
(exwm--log)
(when (derived-mode-p 'exwm-mode)
(dolist (i exwm-manage-configurations)
(save-current-buffer
(when (with-demoted-errors "Problematic configuration: %S"
(eval (car i) t))
(cl-return-from exwm-manage--get-configurations (cdr i)))))))
(defun exwm-manage--manage-window (id)
"Manage window ID."
(exwm--log "Try to manage #x%x" id)
(catch 'return
;; Ensure it's alive
(when (xcb:+request-checked+request-check exwm--connection
(make-instance 'xcb:ChangeWindowAttributes
:window id :value-mask xcb:CW:EventMask
:event-mask (exwm--get-client-event-mask)))
(throw 'return 'dead))
;; Add this X window to save-set.
(xcb:+request exwm--connection
(make-instance 'xcb:ChangeSaveSet
:mode xcb:SetMode:Insert
:window id))
(with-current-buffer (let ((exwm-input--skip-buffer-list-update t))
(generate-new-buffer "*EXWM*"))
;; Keep the oldest X window first.
(setq exwm--id-buffer-alist
(nconc exwm--id-buffer-alist `((,id . ,(current-buffer)))))
(exwm-mode)
(setq exwm--id id
exwm--frame exwm-workspace--current)
(exwm--update-window-type id)
(exwm--update-class id)
(exwm--update-transient-for id)
(exwm--update-normal-hints id)
(exwm--update-hints id)
(exwm-manage--update-geometry id)
(exwm-manage--update-mwm-hints id)
(exwm--update-title id)
(exwm--update-protocols id)
(setq exwm--configurations (exwm-manage--get-configurations))
;; OverrideRedirect is not checked here.
(when (and
;; The user has specified to manage it.
(not (plist-get exwm--configurations 'managed))
(or
;; The user has specified not to manage it.
(plist-member exwm--configurations 'managed)
;; This is not a type of X window we can manage.
(and exwm-window-type
(not (cl-intersection
exwm-window-type
(list xcb:Atom:_NET_WM_WINDOW_TYPE_UTILITY
xcb:Atom:_NET_WM_WINDOW_TYPE_DIALOG
xcb:Atom:_NET_WM_WINDOW_TYPE_NORMAL))))
;; Check the _MOTIF_WM_HINTS property to not manage floating X
;; windows without decoration.
(and (not exwm--mwm-hints-decorations)
(not exwm--hints-input)
;; Floating windows only
(or exwm-transient-for exwm--fixed-size
(memq xcb:Atom:_NET_WM_WINDOW_TYPE_UTILITY
exwm-window-type)
(memq xcb:Atom:_NET_WM_WINDOW_TYPE_DIALOG
exwm-window-type)))))
(exwm--log "No need to manage #x%x" id)
;; Update struts.
(when (memq xcb:Atom:_NET_WM_WINDOW_TYPE_DOCK exwm-window-type)
(exwm--update-struts id))
;; Remove all events
(xcb:+request exwm--connection
(make-instance 'xcb:ChangeWindowAttributes
:window id :value-mask xcb:CW:EventMask
:event-mask
(if (memq xcb:Atom:_NET_WM_WINDOW_TYPE_DOCK
exwm-window-type)
;; Listen for PropertyChange (struts) and
;; UnmapNotify/DestroyNotify event of the dock.
(exwm--get-client-event-mask)
xcb:EventMask:NoEvent)))
;; The window needs to be mapped
(xcb:+request exwm--connection
(make-instance 'xcb:MapWindow :window id))
(with-slots (x y width height) exwm--geometry
;; Center window of type _NET_WM_WINDOW_TYPE_SPLASH
(when (memq xcb:Atom:_NET_WM_WINDOW_TYPE_SPLASH exwm-window-type)
(let* ((workarea (elt exwm-workspace--workareas
(exwm-workspace--position exwm--frame)))
(x* (aref workarea 0))
(y* (aref workarea 1))
(width* (aref workarea 2))
(height* (aref workarea 3)))
(exwm--set-geometry id
(+ x* (/ (- width* width) 2))
(+ y* (/ (- height* height) 2))
nil
nil))))
;; Check for desktop.
(when (memq xcb:Atom:_NET_WM_WINDOW_TYPE_DESKTOP exwm-window-type)
;; There should be only one desktop X window.
(setq exwm-manage--desktop id)
;; Put it at bottom.
(xcb:+request exwm--connection
(make-instance 'xcb:ConfigureWindow
:window id
:value-mask xcb:ConfigWindow:StackMode
:stack-mode xcb:StackMode:Below)))
(xcb:flush exwm--connection)
(setq exwm--id-buffer-alist (assq-delete-all id exwm--id-buffer-alist))
(let ((kill-buffer-query-functions nil)
(exwm-input--skip-buffer-list-update t))
(kill-buffer (current-buffer)))
(throw 'return 'ignored))
(let ((index (plist-get exwm--configurations 'workspace)))
(when (and index (< index (length exwm-workspace--list)))
(setq exwm--frame (elt exwm-workspace--list index))))
;; Manage the window
(exwm--log "Manage #x%x" id)
(xcb:+request exwm--connection ;remove border
(make-instance 'xcb:ConfigureWindow
:window id :value-mask xcb:ConfigWindow:BorderWidth
:border-width 0))
(dolist (button ;grab buttons to set focus / move / resize
(list xcb:ButtonIndex:1 xcb:ButtonIndex:2 xcb:ButtonIndex:3))
(xcb:+request exwm--connection
(make-instance 'xcb:GrabButton
:owner-events 0 :grab-window id
:event-mask xcb:EventMask:ButtonPress
:pointer-mode xcb:GrabMode:Sync
:keyboard-mode xcb:GrabMode:Async
:confine-to xcb:Window:None :cursor xcb:Cursor:None
:button button :modifiers xcb:ModMask:Any)))
(exwm-manage--set-client-list)
(xcb:flush exwm--connection)
(if (plist-member exwm--configurations 'floating)
;; User has specified whether it should be floating.
(if (plist-get exwm--configurations 'floating)
(exwm-floating--set-floating id)
(with-selected-window (frame-selected-window exwm--frame)
(exwm-floating--unset-floating id)))
;; Try to determine if it should be floating.
(if (and (not exwm-manage-force-tiling)
(or exwm-transient-for exwm--fixed-size
(memq xcb:Atom:_NET_WM_WINDOW_TYPE_UTILITY
exwm-window-type)
(memq xcb:Atom:_NET_WM_WINDOW_TYPE_DIALOG
exwm-window-type)))
(exwm-floating--set-floating id)
(with-selected-window (frame-selected-window exwm--frame)
(exwm-floating--unset-floating id))))
(if (plist-get exwm--configurations 'char-mode)
(exwm-input-release-keyboard id)
(exwm-input-grab-keyboard id))
(let ((simulation-keys (plist-get exwm--configurations 'simulation-keys))
(prefix-keys (plist-get exwm--configurations 'prefix-keys)))
(with-current-buffer (exwm--id->buffer id)
(when simulation-keys
(exwm-input-set-local-simulation-keys simulation-keys))
(when prefix-keys
(setq-local exwm-input-prefix-keys prefix-keys))))
(setq exwm-workspace--switch-history-outdated t)
(exwm--update-desktop id)
(exwm-manage--update-ewmh-state id)
(with-current-buffer (exwm--id->buffer id)
(when (or (plist-get exwm--configurations 'fullscreen)
(exwm-layout--fullscreen-p))
(setq exwm--ewmh-state (delq xcb:Atom:_NET_WM_STATE_FULLSCREEN
exwm--ewmh-state))
(exwm-layout-set-fullscreen id))
(run-hooks 'exwm-manage-finish-hook)))))
(defun exwm-manage--unmanage-window (id &optional withdraw-only)
"Unmanage window ID.
If WITHDRAW-ONLY is non-nil, the X window will be properly placed back to the
root window. Set WITHDRAW-ONLY to 'quit if this functions is used when window
manager is shutting down."
(let ((buffer (exwm--id->buffer id)))
(exwm--log "Unmanage #x%x (buffer: %s, widthdraw: %s)"
id buffer withdraw-only)
(setq exwm--id-buffer-alist (assq-delete-all id exwm--id-buffer-alist))
;; Update workspaces when a dock is destroyed.
(when (and (null withdraw-only)
(assq id exwm-workspace--id-struts-alist))
(setq exwm-workspace--id-struts-alist
(assq-delete-all id exwm-workspace--id-struts-alist))
(exwm-workspace--update-struts)
(exwm-workspace--update-workareas)
(dolist (f exwm-workspace--list)
(exwm-workspace--set-fullscreen f)))
(when (buffer-live-p buffer)
(with-current-buffer buffer
;; Unmap the X window.
(xcb:+request exwm--connection
(make-instance 'xcb:UnmapWindow :window id))
;;
(setq exwm-workspace--switch-history-outdated t)
;;
(when withdraw-only
(xcb:+request exwm--connection
(make-instance 'xcb:ChangeWindowAttributes
:window id :value-mask xcb:CW:EventMask
:event-mask xcb:EventMask:NoEvent))
;; Delete WM_STATE property
(xcb:+request exwm--connection
(make-instance 'xcb:DeleteProperty
:window id :property xcb:Atom:WM_STATE))
(cond
((eq withdraw-only 'quit)
;; Remap the window when exiting.
(xcb:+request exwm--connection
(make-instance 'xcb:MapWindow :window id)))
(t
;; Remove _NET_WM_DESKTOP.
(xcb:+request exwm--connection
(make-instance 'xcb:DeleteProperty
:window id
:property xcb:Atom:_NET_WM_DESKTOP)))))
(when exwm--floating-frame
;; Unmap the floating frame before destroying its container.
(let ((window (frame-parameter exwm--floating-frame 'exwm-outer-id))
(container (frame-parameter exwm--floating-frame
'exwm-container)))
(xcb:+request exwm--connection
(make-instance 'xcb:UnmapWindow :window window))
(xcb:+request exwm--connection
(make-instance 'xcb:ReparentWindow
:window window :parent exwm--root :x 0 :y 0))
(xcb:+request exwm--connection
(make-instance 'xcb:DestroyWindow :window container))))
(when (exwm-layout--fullscreen-p)
(let ((window (get-buffer-window)))
(when window
(set-window-dedicated-p window nil))))
(exwm-manage--set-client-list)
(xcb:flush exwm--connection))
(let ((kill-buffer-func
(lambda (buffer)
(when (buffer-local-value 'exwm--floating-frame buffer)
(select-window
(frame-selected-window exwm-workspace--current)))
(with-current-buffer buffer
(let ((kill-buffer-query-functions nil))
(kill-buffer buffer))))))
(exwm--defer 0 kill-buffer-func buffer)
(when (active-minibuffer-window)
(exit-minibuffer))))))
(defun exwm-manage--scan ()
"Search for existing windows and try to manage them."
(exwm--log)
(let* ((tree (xcb:+request-unchecked+reply exwm--connection
(make-instance 'xcb:QueryTree
:window exwm--root)))
reply)
(dolist (i (slot-value tree 'children))
(setq reply (xcb:+request-unchecked+reply exwm--connection
(make-instance 'xcb:GetWindowAttributes
:window i)))
;; It's possible the X window has been destroyed.
(when reply
(with-slots (override-redirect map-state) reply
(when (and (= 0 override-redirect)
(= xcb:MapState:Viewable map-state))
(xcb:+request exwm--connection
(make-instance 'xcb:UnmapWindow
:window i))
(xcb:flush exwm--connection)
(exwm-manage--manage-window i)))))))
(defun exwm-manage--kill-buffer-query-function ()
"Run in `kill-buffer-query-functions'."
(exwm--log "id=#x%x; buffer=%s" exwm--id (current-buffer))
(catch 'return
(when (or (not exwm--id)
(xcb:+request-checked+request-check exwm--connection
(make-instance 'xcb:ChangeWindowAttributes
:window exwm--id
:value-mask xcb:CW:EventMask
:event-mask (exwm--get-client-event-mask))))
;; The X window is no longer alive so just close the buffer.
(when exwm--floating-frame
(let ((window (frame-parameter exwm--floating-frame 'exwm-outer-id))
(container (frame-parameter exwm--floating-frame
'exwm-container)))
(xcb:+request exwm--connection
(make-instance 'xcb:UnmapWindow :window window))
(xcb:+request exwm--connection
(make-instance 'xcb:ReparentWindow
:window window
:parent exwm--root
:x 0 :y 0))
(xcb:+request exwm--connection
(make-instance 'xcb:DestroyWindow
:window container))))
(xcb:flush exwm--connection)
(throw 'return t))
(unless (memq xcb:Atom:WM_DELETE_WINDOW exwm--protocols)
;; The X window does not support WM_DELETE_WINDOW; destroy it.
(xcb:+request exwm--connection
(make-instance 'xcb:DestroyWindow :window exwm--id))
(xcb:flush exwm--connection)
;; Wait for DestroyNotify event.
(throw 'return nil))
(let ((id exwm--id))
;; Try to close the X window with WM_DELETE_WINDOW client message.
(xcb:+request exwm--connection
(make-instance 'xcb:icccm:SendEvent
:destination id
:event (xcb:marshal
(make-instance 'xcb:icccm:WM_DELETE_WINDOW
:window id)
exwm--connection)))
(xcb:flush exwm--connection)
;;
(unless (memq xcb:Atom:_NET_WM_PING exwm--protocols)
;; For X windows without _NET_WM_PING support, we'd better just
;; wait for DestroyNotify events.
(throw 'return nil))
;; Try to determine if the X window is dead with _NET_WM_PING.
(setq exwm-manage--ping-lock t)
(xcb:+request exwm--connection
(make-instance 'xcb:SendEvent
:propagate 0
:destination id
:event-mask xcb:EventMask:NoEvent
:event (xcb:marshal
(make-instance 'xcb:ewmh:_NET_WM_PING
:window id
:timestamp 0
:client-window id)
exwm--connection)))
(xcb:flush exwm--connection)
(with-timeout (exwm-manage-ping-timeout
(if (y-or-n-p (format "'%s' is not responding. \
Would you like to kill it? "
(buffer-name)))
(progn (exwm-manage--kill-client id)
;; Kill the unresponsive X window and
;; wait for DestroyNotify event.
(throw 'return nil))
;; Give up.
(throw 'return nil)))
(while (and exwm-manage--ping-lock
(exwm--id->buffer id)) ;may have been destroyed.
(accept-process-output nil 0.1))
;; Give up.
(throw 'return nil)))))
(defun exwm-manage--kill-client (&optional id)
"Kill an X client."
(unless id (setq id (exwm--buffer->id (current-buffer))))
(exwm--log "id=#x%x" id)
(let* ((response (xcb:+request-unchecked+reply exwm--connection
(make-instance 'xcb:ewmh:get-_NET_WM_PID :window id)))
(pid (and response (slot-value response 'value)))
(request (make-instance 'xcb:KillClient :resource id)))
(if (not pid)
(xcb:+request exwm--connection request)
;; What if the PID is fake/wrong?
(signal-process pid 'SIGKILL)
;; Ensure it's dead
(run-with-timer exwm-manage-ping-timeout nil
(lambda ()
(xcb:+request exwm--connection request))))
(xcb:flush exwm--connection)))
(defun exwm-manage--add-frame (frame)
"Run in `after-make-frame-functions'."
(exwm--log "frame=%s" frame)
(when (display-graphic-p frame)
(push (string-to-number (frame-parameter frame 'outer-window-id))
exwm-manage--frame-outer-id-list)))
(defun exwm-manage--remove-frame (frame)
"Run in `delete-frame-functions'."
(exwm--log "frame=%s" frame)
(when (display-graphic-p frame)
(setq exwm-manage--frame-outer-id-list
(delq (string-to-number (frame-parameter frame 'outer-window-id))
exwm-manage--frame-outer-id-list))))
(defun exwm-manage--on-ConfigureRequest (data _synthetic)
"Handle ConfigureRequest event."
(exwm--log)
(let ((obj (make-instance 'xcb:ConfigureRequest))
buffer edges width-delta height-delta)
(xcb:unmarshal obj data)
(with-slots (window x y width height
border-width sibling stack-mode value-mask)
obj
(exwm--log "#x%x (#x%x) @%dx%d%+d%+d; \
border-width: %d; sibling: #x%x; stack-mode: %d"
window value-mask width height x y
border-width sibling stack-mode)
(if (and (setq buffer (exwm--id->buffer window))
(with-current-buffer buffer
(or (exwm-layout--fullscreen-p)
;; Make sure it's a floating X window wanting to resize
;; itself.
(or (not exwm--floating-frame)
(progn
(setq edges
(window-inside-pixel-edges
(get-buffer-window buffer t))
width-delta (- width (- (elt edges 2)
(elt edges 0)))
height-delta (- height (- (elt edges 3)
(elt edges 1))))
;; We cannot do resizing precisely for now.
(and (if (= 0 (logand value-mask
xcb:ConfigWindow:Width))
t
(< (abs width-delta)
exwm-manage--width-delta-min))
(if (= 0 (logand value-mask
xcb:ConfigWindow:Height))
t
(< (abs height-delta)
exwm-manage--height-delta-min))))))))
;; Send client message for managed windows
(with-current-buffer buffer
(setq edges
(if (exwm-layout--fullscreen-p)
(with-slots (x y width height)
(exwm-workspace--get-geometry exwm--frame)
(list x y width height))
(window-inside-absolute-pixel-edges
(get-buffer-window buffer t))))
(exwm--log "Reply with ConfigureNotify (edges): %s" edges)
(xcb:+request exwm--connection
(make-instance 'xcb:SendEvent
:propagate 0 :destination window
:event-mask xcb:EventMask:StructureNotify
:event (xcb:marshal
(make-instance
'xcb:ConfigureNotify
:event window :window window
:above-sibling xcb:Window:None
:x (elt edges 0) :y (elt edges 1)
:width (- (elt edges 2) (elt edges 0))
:height (- (elt edges 3) (elt edges 1))
:border-width 0 :override-redirect 0)
exwm--connection))))
(if buffer
(with-current-buffer buffer
(exwm--log "ConfigureWindow (resize floating X window)")
(exwm--set-geometry (frame-parameter exwm--floating-frame
'exwm-outer-id)
nil
nil
(+ (frame-pixel-width exwm--floating-frame)
width-delta)
(+ (frame-pixel-height exwm--floating-frame)
height-delta)))
(exwm--log "ConfigureWindow (preserve geometry)")
;; Configure the unmanaged window.
;; But Emacs frames should be excluded. Generally we don't
;; receive ConfigureRequest events from Emacs frames since we
;; have set OverrideRedirect on them, but this is not true for
;; Lucid build (as of 25.1).
(unless (memq window exwm-manage--frame-outer-id-list)
(xcb:+request exwm--connection
(make-instance 'xcb:ConfigureWindow
:window window
:value-mask value-mask
:x x :y y :width width :height height
:border-width border-width
:sibling sibling
:stack-mode stack-mode)))))))
(xcb:flush exwm--connection))
(defun exwm-manage--on-MapRequest (data _synthetic)
"Handle MapRequest event."
(let ((obj (make-instance 'xcb:MapRequest)))
(xcb:unmarshal obj data)
(with-slots (parent window) obj
(exwm--log "id=#x%x parent=#x%x" window parent)
(if (assoc window exwm--id-buffer-alist)
(with-current-buffer (exwm--id->buffer window)
(if (exwm-layout--iconic-state-p)
;; State change: iconic => normal.
(when (eq exwm--frame exwm-workspace--current)
(pop-to-buffer-same-window (current-buffer)))
(exwm--log "#x%x is already managed" window)))
(if (/= exwm--root parent)
(progn (xcb:+request exwm--connection
(make-instance 'xcb:MapWindow :window window))
(xcb:flush exwm--connection))
(exwm--log "#x%x" window)
(exwm-manage--manage-window window))))))
(defun exwm-manage--on-UnmapNotify (data _synthetic)
"Handle UnmapNotify event."
(let ((obj (make-instance 'xcb:UnmapNotify)))
(xcb:unmarshal obj data)
(with-slots (window) obj
(exwm--log "id=#x%x" window)
(exwm-manage--unmanage-window window t))))
(defun exwm-manage--on-MapNotify (data _synthetic)
"Handle MapNotify event."
(let ((obj (make-instance 'xcb:MapNotify)))
(xcb:unmarshal obj data)
(with-slots (window) obj
(when (assoc window exwm--id-buffer-alist)
(exwm--log "id=#x%x" window)
;; With this we ensure that a "window hierarchy change" happens after
;; mapping the window, as some servers (XQuartz) do not generate it.
(with-current-buffer (exwm--id->buffer window)
(if exwm--floating-frame
(xcb:+request exwm--connection
(make-instance 'xcb:ConfigureWindow
:window window
:value-mask xcb:ConfigWindow:StackMode
:stack-mode xcb:StackMode:Above))
(xcb:+request exwm--connection
(make-instance 'xcb:ConfigureWindow
:window window
:value-mask (logior xcb:ConfigWindow:Sibling
xcb:ConfigWindow:StackMode)
:sibling exwm--guide-window
:stack-mode xcb:StackMode:Above))))
(xcb:flush exwm--connection)))))
(defun exwm-manage--on-DestroyNotify (data synthetic)
"Handle DestroyNotify event."
(unless synthetic
(exwm--log)
(let ((obj (make-instance 'xcb:DestroyNotify)))
(xcb:unmarshal obj data)
(exwm--log "#x%x" (slot-value obj 'window))
(exwm-manage--unmanage-window (slot-value obj 'window)))))
(defun exwm-manage--init ()
"Initialize manage module."
;; Intern _MOTIF_WM_HINTS
(exwm--log)
(setq exwm-manage--_MOTIF_WM_HINTS (exwm--intern-atom "_MOTIF_WM_HINTS"))
(add-hook 'after-make-frame-functions #'exwm-manage--add-frame)
(add-hook 'delete-frame-functions #'exwm-manage--remove-frame)
(xcb:+event exwm--connection 'xcb:ConfigureRequest
#'exwm-manage--on-ConfigureRequest)
(xcb:+event exwm--connection 'xcb:MapRequest #'exwm-manage--on-MapRequest)
(xcb:+event exwm--connection 'xcb:UnmapNotify #'exwm-manage--on-UnmapNotify)
(xcb:+event exwm--connection 'xcb:MapNotify #'exwm-manage--on-MapNotify)
(xcb:+event exwm--connection 'xcb:DestroyNotify
#'exwm-manage--on-DestroyNotify))
(defun exwm-manage--exit ()
"Exit the manage module."
(exwm--log)
(dolist (pair exwm--id-buffer-alist)
(exwm-manage--unmanage-window (car pair) 'quit))
(remove-hook 'after-make-frame-functions #'exwm-manage--add-frame)
(remove-hook 'delete-frame-functions #'exwm-manage--remove-frame)
(setq exwm-manage--_MOTIF_WM_HINTS nil))
(provide 'exwm-manage)
;;; exwm-manage.el ends here

Binary file not shown.

View File

@ -1,2 +0,0 @@
;; Generated package description from exwm.el -*- no-byte-compile: t -*-
(define-package "exwm" "0.24" "Emacs X Window Manager" '((xelb "0.18")) :keywords '("unix") :authors '(("Chris Feng" . "chris.w.feng@gmail.com")) :maintainer '("Chris Feng" . "chris.w.feng@gmail.com") :url "https://github.com/ch11ng/exwm")

View File

@ -1,375 +0,0 @@
;;; exwm-randr.el --- RandR Module for EXWM -*- lexical-binding: t -*-
;; Copyright (C) 2015-2020 Free Software Foundation, Inc.
;; Author: Chris Feng <chris.w.feng@gmail.com>
;; This file is part of GNU Emacs.
;; GNU Emacs 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 3 of the License, or
;; (at your option) any later version.
;; GNU Emacs 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 GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
;;; Commentary:
;; This module adds RandR support for EXWM. Currently it requires external
;; tools such as xrandr(1) to properly configure RandR first. This
;; dependency may be removed in the future, but more work is needed before
;; that.
;; To use this module, load, enable it and configure
;; `exwm-randr-workspace-monitor-plist' and `exwm-randr-screen-change-hook'
;; as follows:
;;
;; (require 'exwm-randr)
;; (setq exwm-randr-workspace-monitor-plist '(0 "VGA1"))
;; (add-hook 'exwm-randr-screen-change-hook
;; (lambda ()
;; (start-process-shell-command
;; "xrandr" nil "xrandr --output VGA1 --left-of LVDS1 --auto")))
;; (exwm-randr-enable)
;;
;; With above lines, workspace 0 should be assigned to the output named "VGA1",
;; staying at the left of other workspaces on the output "LVDS1". Please refer
;; to xrandr(1) for the configuration of RandR.
;; References:
;; + RandR (http://www.x.org/archive/X11R7.7/doc/randrproto/randrproto.txt)
;;; Code:
(require 'xcb-randr)
(require 'exwm-core)
(require 'exwm-workspace)
(defgroup exwm-randr nil
"RandR."
:version "25.3"
:group 'exwm)
(defcustom exwm-randr-refresh-hook nil
"Normal hook run when the RandR module just refreshed."
:type 'hook)
(defcustom exwm-randr-screen-change-hook nil
"Normal hook run when screen changes."
:type 'hook)
(defcustom exwm-randr-workspace-monitor-plist nil
"Plist mapping workspaces to monitors.
In RandR 1.5 a monitor is a rectangle region decoupled from the physical
size of screens, and can be identified with `xrandr --listmonitors' (name of
the primary monitor is prefixed with an `*'). When no monitor is created it
automatically fallback to RandR 1.2 output which represents the physical
screen size. RandR 1.5 monitors can be created with `xrandr --setmonitor'.
For example, to split an output (`LVDS-1') of size 1280x800 into two
side-by-side monitors one could invoke (the digits after `/' are size in mm)
xrandr --setmonitor *LVDS-1-L 640/135x800/163+0+0 LVDS-1
xrandr --setmonitor LVDS-1-R 640/135x800/163+640+0 none
If a monitor is not active, the workspaces mapped to it are displayed on the
primary monitor until it becomes active (if ever). Unspecified workspaces
are all mapped to the primary monitor. For example, with the following
setting workspace other than 1 and 3 would always be displayed on the
primary monitor where workspace 1 and 3 would be displayed on their
corresponding monitors whenever the monitors are active.
\\='(1 \"HDMI-1\" 3 \"DP-1\")"
:type '(plist :key-type integer :value-type string))
(with-no-warnings
(define-obsolete-variable-alias 'exwm-randr-workspace-output-plist
'exwm-randr-workspace-monitor-plist "27.1"))
(defvar exwm-randr--last-timestamp 0 "Used for debouncing events.")
(defvar exwm-randr--prev-screen-change-seqnum nil
"The most recent ScreenChangeNotify sequence number.")
(defvar exwm-randr--compatibility-mode nil
"Non-nil when the server does not support RandR 1.5 protocol.")
(defun exwm-randr--get-monitors ()
"Get RandR 1.5 monitors."
(exwm--log)
(let (monitor-name geometry monitor-geometry-alist primary-monitor)
(with-slots (timestamp monitors)
(xcb:+request-unchecked+reply exwm--connection
(make-instance 'xcb:randr:GetMonitors
:window exwm--root
:get-active 1))
(when (> timestamp exwm-randr--last-timestamp)
(setq exwm-randr--last-timestamp timestamp))
(dolist (monitor monitors)
(with-slots (name primary x y width height) monitor
(setq monitor-name (x-get-atom-name name)
geometry (make-instance 'xcb:RECTANGLE
:x x
:y y
:width width
:height height)
monitor-geometry-alist (cons (cons monitor-name geometry)
monitor-geometry-alist))
(exwm--log "%s: %sx%s+%s+%s" monitor-name x y width height)
;; Save primary monitor when available (fallback to the first one).
(when (or (/= 0 primary)
(not primary-monitor))
(setq primary-monitor monitor-name)))))
(exwm--log "Primary monitor: %s" primary-monitor)
(list primary-monitor monitor-geometry-alist
(exwm-randr--get-monitor-alias primary-monitor
monitor-geometry-alist))))
(defun exwm-randr--get-outputs ()
"Get RandR 1.2 outputs.
Only used when RandR 1.5 is not supported by the server."
(exwm--log)
(let (output-name geometry output-geometry-alist primary-output)
(with-slots (config-timestamp outputs)
(xcb:+request-unchecked+reply exwm--connection
(make-instance 'xcb:randr:GetScreenResourcesCurrent
:window exwm--root))
(when (> config-timestamp exwm-randr--last-timestamp)
(setq exwm-randr--last-timestamp config-timestamp))
(dolist (output outputs)
(with-slots (crtc connection name)
(xcb:+request-unchecked+reply exwm--connection
(make-instance 'xcb:randr:GetOutputInfo
:output output
:config-timestamp config-timestamp))
(when (and (= connection xcb:randr:Connection:Connected)
(/= crtc 0))
(with-slots (x y width height)
(xcb:+request-unchecked+reply exwm--connection
(make-instance 'xcb:randr:GetCrtcInfo
:crtc crtc
:config-timestamp config-timestamp))
(setq output-name (decode-coding-string
(apply #'unibyte-string name) 'utf-8)
geometry (make-instance 'xcb:RECTANGLE
:x x
:y y
:width width
:height height)
output-geometry-alist (cons (cons output-name geometry)
output-geometry-alist))
(exwm--log "%s: %sx%s+%s+%s" output-name x y width height)
;; The primary output is the first one.
(unless primary-output
(setq primary-output output-name)))))))
(exwm--log "Primary output: %s" primary-output)
(list primary-output output-geometry-alist
(exwm-randr--get-monitor-alias primary-output
output-geometry-alist))))
(defun exwm-randr--get-monitor-alias (primary-monitor monitor-geometry-alist)
"Generate monitor aliases using PRIMARY-MONITOR MONITOR-GEOMETRY-ALIST.
In a mirroring setup some monitors overlap and should be treated as one."
(let (monitor-position-alist monitor-alias-alist monitor-name geometry)
(setq monitor-position-alist (with-slots (x y)
(cdr (assoc primary-monitor
monitor-geometry-alist))
(list (cons primary-monitor (vector x y)))))
(setq monitor-alias-alist (list (cons primary-monitor primary-monitor)))
(dolist (pair monitor-geometry-alist)
(setq monitor-name (car pair)
geometry (cdr pair))
(unless (assoc monitor-name monitor-alias-alist)
(let* ((position (vector (slot-value geometry 'x)
(slot-value geometry 'y)))
(alias (car (rassoc position monitor-position-alist))))
(if alias
(setq monitor-alias-alist (cons (cons monitor-name alias)
monitor-alias-alist))
(setq monitor-position-alist (cons (cons monitor-name position)
monitor-position-alist)
monitor-alias-alist (cons (cons monitor-name monitor-name)
monitor-alias-alist))))))
monitor-alias-alist))
;;;###autoload
(defun exwm-randr-refresh ()
"Refresh workspaces according to the updated RandR info."
(interactive)
(exwm--log)
(let* ((result (if exwm-randr--compatibility-mode
(exwm-randr--get-outputs)
(exwm-randr--get-monitors)))
(primary-monitor (elt result 0))
(monitor-geometry-alist (elt result 1))
(monitor-alias-alist (elt result 2))
container-monitor-alist container-frame-alist)
(when (and primary-monitor monitor-geometry-alist)
(when exwm-workspace--fullscreen-frame-count
;; Not all workspaces are fullscreen; reset this counter.
(setq exwm-workspace--fullscreen-frame-count 0))
(dotimes (i (exwm-workspace--count))
(let* ((monitor (plist-get exwm-randr-workspace-monitor-plist i))
(geometry (cdr (assoc monitor monitor-geometry-alist)))
(frame (elt exwm-workspace--list i))
(container (frame-parameter frame 'exwm-container)))
(if geometry
;; Unify monitor names in case it's a mirroring setup.
(setq monitor (cdr (assoc monitor monitor-alias-alist)))
;; Missing monitors fallback to the primary one.
(setq monitor primary-monitor
geometry (cdr (assoc primary-monitor
monitor-geometry-alist))))
(setq container-monitor-alist (nconc
`((,container . ,(intern monitor)))
container-monitor-alist)
container-frame-alist (nconc `((,container . ,frame))
container-frame-alist))
(set-frame-parameter frame 'exwm-randr-monitor monitor)
(set-frame-parameter frame 'exwm-geometry geometry)))
;; Update workareas.
(exwm-workspace--update-workareas)
;; Resize workspace.
(dolist (f exwm-workspace--list)
(exwm-workspace--set-fullscreen f))
(xcb:flush exwm--connection)
;; Raise the minibuffer if it's active.
(when (and (active-minibuffer-window)
(exwm-workspace--minibuffer-own-frame-p))
(exwm-workspace--show-minibuffer))
;; Set _NET_DESKTOP_GEOMETRY.
(exwm-workspace--set-desktop-geometry)
;; Update active/inactive workspaces.
(dolist (w exwm-workspace--list)
(exwm-workspace--set-active w nil))
;; Mark the workspace on the top of each monitor as active.
(dolist (xwin
(reverse
(slot-value (xcb:+request-unchecked+reply exwm--connection
(make-instance 'xcb:QueryTree
:window exwm--root))
'children)))
(let ((monitor (cdr (assq xwin container-monitor-alist))))
(when monitor
(setq container-monitor-alist
(rassq-delete-all monitor container-monitor-alist))
(exwm-workspace--set-active (cdr (assq xwin container-frame-alist))
t))))
(xcb:flush exwm--connection)
(run-hooks 'exwm-randr-refresh-hook))))
(define-obsolete-function-alias 'exwm-randr--refresh #'exwm-randr-refresh
"27.1")
(defun exwm-randr--on-ScreenChangeNotify (data _synthetic)
"Handle `ScreenChangeNotify' event.
Run `exwm-randr-screen-change-hook' (usually user scripts to configure RandR)."
(exwm--log)
(let ((evt (make-instance 'xcb:randr:ScreenChangeNotify)))
(xcb:unmarshal evt data)
(let ((seqnum (slot-value evt '~sequence)))
(unless (equal seqnum exwm-randr--prev-screen-change-seqnum)
(setq exwm-randr--prev-screen-change-seqnum seqnum)
(run-hooks 'exwm-randr-screen-change-hook)))))
(defun exwm-randr--on-Notify (data _synthetic)
"Handle `CrtcChangeNotify' and `OutputChangeNotify' events.
Refresh when any CRTC/output changes."
(exwm--log)
(let ((evt (make-instance 'xcb:randr:Notify))
notify)
(xcb:unmarshal evt data)
(with-slots (subCode u) evt
(cl-case subCode
(xcb:randr:Notify:CrtcChange
(setq notify (slot-value u 'cc)))
(xcb:randr:Notify:OutputChange
(setq notify (slot-value u 'oc))))
(when notify
(with-slots (timestamp) notify
(when (> timestamp exwm-randr--last-timestamp)
(exwm-randr-refresh)
(setq exwm-randr--last-timestamp timestamp)))))))
(defun exwm-randr--on-ConfigureNotify (data _synthetic)
"Handle `ConfigureNotify' event.
Refresh when any RandR 1.5 monitor changes."
(exwm--log)
(let ((evt (make-instance 'xcb:ConfigureNotify)))
(xcb:unmarshal evt data)
(with-slots (window) evt
(when (eq window exwm--root)
(exwm-randr-refresh)))))
(defun exwm-randr--init ()
"Initialize RandR extension and EXWM RandR module."
(exwm--log)
(when (= 0 (slot-value (xcb:get-extension-data exwm--connection 'xcb:randr)
'present))
(error "[EXWM] RandR extension is not supported by the server"))
(with-slots (major-version minor-version)
(xcb:+request-unchecked+reply exwm--connection
(make-instance 'xcb:randr:QueryVersion
:major-version 1 :minor-version 5))
(cond ((and (= major-version 1) (= minor-version 5))
(setq exwm-randr--compatibility-mode nil))
((and (= major-version 1) (>= minor-version 2))
(setq exwm-randr--compatibility-mode t))
(t
(error "[EXWM] The server only support RandR version up to %d.%d"
major-version minor-version)))
;; External monitor(s) may already be connected.
(run-hooks 'exwm-randr-screen-change-hook)
(exwm-randr-refresh)
;; Listen for `ScreenChangeNotify' to notify external tools to
;; configure RandR and `CrtcChangeNotify/OutputChangeNotify' to
;; refresh the workspace layout.
(xcb:+event exwm--connection 'xcb:randr:ScreenChangeNotify
#'exwm-randr--on-ScreenChangeNotify)
(xcb:+event exwm--connection 'xcb:randr:Notify
#'exwm-randr--on-Notify)
(xcb:+event exwm--connection 'xcb:ConfigureNotify
#'exwm-randr--on-ConfigureNotify)
(xcb:+request exwm--connection
(make-instance 'xcb:randr:SelectInput
:window exwm--root
:enable (logior
xcb:randr:NotifyMask:ScreenChange
xcb:randr:NotifyMask:CrtcChange
xcb:randr:NotifyMask:OutputChange)))
(xcb:flush exwm--connection)
(add-hook 'exwm-workspace-list-change-hook #'exwm-randr-refresh))
;; Prevent frame parameters introduced by this module from being
;; saved/restored.
(dolist (i '(exwm-randr-monitor))
(unless (assq i frameset-filter-alist)
(push (cons i :never) frameset-filter-alist))))
(defun exwm-randr--exit ()
"Exit the RandR module."
(exwm--log)
(remove-hook 'exwm-workspace-list-change-hook #'exwm-randr-refresh))
(defun exwm-randr-enable ()
"Enable RandR support for EXWM."
(exwm--log)
(add-hook 'exwm-init-hook #'exwm-randr--init)
(add-hook 'exwm-exit-hook #'exwm-randr--exit))
(provide 'exwm-randr)
;;; exwm-randr.el ends here

Binary file not shown.

View File

@ -1,587 +0,0 @@
;;; exwm-systemtray.el --- System Tray Module for -*- lexical-binding: t -*-
;;; EXWM
;; Copyright (C) 2016-2020 Free Software Foundation, Inc.
;; Author: Chris Feng <chris.w.feng@gmail.com>
;; This file is part of GNU Emacs.
;; GNU Emacs 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 3 of the License, or
;; (at your option) any later version.
;; GNU Emacs 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 GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
;;; Commentary:
;; This module adds system tray support for EXWM.
;; To use this module, load and enable it as follows:
;; (require 'exwm-systemtray)
;; (exwm-systemtray-enable)
;;; Code:
(require 'xcb-icccm)
(require 'xcb-xembed)
(require 'xcb-systemtray)
(require 'exwm-core)
(require 'exwm-workspace)
(defclass exwm-systemtray--icon ()
((width :initarg :width)
(height :initarg :height)
(visible :initarg :visible))
:documentation "Attributes of a system tray icon.")
(defclass xcb:systemtray:-ClientMessage
(xcb:icccm:--ClientMessage xcb:ClientMessage)
((format :initform 32)
(type :initform xcb:Atom:MANAGER)
(time :initarg :time :type xcb:TIMESTAMP) ;new slot
(selection :initarg :selection :type xcb:ATOM) ;new slot
(owner :initarg :owner :type xcb:WINDOW)) ;new slot
:documentation "A systemtray client message.")
(defgroup exwm-systemtray nil
"System tray."
:version "25.3"
:group 'exwm)
(defcustom exwm-systemtray-height nil
"System tray height.
You shall use the default value if using auto-hide minibuffer."
:type 'integer)
(defcustom exwm-systemtray-icon-gap 2
"Gap between icons."
:type 'integer)
(defvar exwm-systemtray--embedder-window nil "The embedder window.")
(defcustom exwm-systemtray-background-color nil
"Background color of systemtray.
This should be a color, or nil for transparent background."
:type '(choice (const :tag "Transparent" nil)
(color))
:initialize #'custom-initialize-default
:set (lambda (symbol value)
(set-default symbol value)
;; Change the background color for embedder.
(when (and exwm--connection
exwm-systemtray--embedder-window)
(let ((background-pixel (exwm--color->pixel value)))
(xcb:+request exwm--connection
(make-instance 'xcb:ChangeWindowAttributes
:window exwm-systemtray--embedder-window
:value-mask (logior xcb:CW:BackPixmap
(if background-pixel
xcb:CW:BackPixel 0))
:background-pixmap
xcb:BackPixmap:ParentRelative
:background-pixel background-pixel))
;; Unmap & map to take effect immediately.
(xcb:+request exwm--connection
(make-instance 'xcb:UnmapWindow
:window exwm-systemtray--embedder-window))
(xcb:+request exwm--connection
(make-instance 'xcb:MapWindow
:window exwm-systemtray--embedder-window))
(xcb:flush exwm--connection)))))
;; GTK icons require at least 16 pixels to show normally.
(defconst exwm-systemtray--icon-min-size 16 "Minimum icon size.")
(defvar exwm-systemtray--connection nil "The X connection.")
(defvar exwm-systemtray--list nil "The icon list.")
(defvar exwm-systemtray--selection-owner-window nil
"The selection owner window.")
(defvar xcb:Atom:_NET_SYSTEM_TRAY_S0)
(defun exwm-systemtray--embed (icon)
"Embed an icon."
(exwm--log "Try to embed #x%x" icon)
(let ((info (xcb:+request-unchecked+reply exwm-systemtray--connection
(make-instance 'xcb:xembed:get-_XEMBED_INFO
:window icon)))
width* height* visible)
(when info
(exwm--log "Embed #x%x" icon)
(with-slots (width height)
(xcb:+request-unchecked+reply exwm-systemtray--connection
(make-instance 'xcb:GetGeometry :drawable icon))
(setq height* exwm-systemtray-height
width* (round (* width (/ (float height*) height))))
(when (< width* exwm-systemtray--icon-min-size)
(setq width* exwm-systemtray--icon-min-size
height* (round (* height (/ (float width*) width)))))
(exwm--log "Resize from %dx%d to %dx%d"
width height width* height*))
;; Add this icon to save-set.
(xcb:+request exwm-systemtray--connection
(make-instance 'xcb:ChangeSaveSet
:mode xcb:SetMode:Insert
:window icon))
;; Reparent to the embedder.
(xcb:+request exwm-systemtray--connection
(make-instance 'xcb:ReparentWindow
:window icon
:parent exwm-systemtray--embedder-window
:x 0
;; Vertically centered.
:y (/ (- exwm-systemtray-height height*) 2)))
;; Resize the icon.
(xcb:+request exwm-systemtray--connection
(make-instance 'xcb:ConfigureWindow
:window icon
:value-mask (logior xcb:ConfigWindow:Width
xcb:ConfigWindow:Height
xcb:ConfigWindow:BorderWidth)
:width width*
:height height*
:border-width 0))
;; Set event mask.
(xcb:+request exwm-systemtray--connection
(make-instance 'xcb:ChangeWindowAttributes
:window icon
:value-mask xcb:CW:EventMask
:event-mask (logior xcb:EventMask:ResizeRedirect
xcb:EventMask:KeyPress
xcb:EventMask:PropertyChange)))
;; Grab all keys and forward them to Emacs frame.
(unless (exwm-workspace--minibuffer-own-frame-p)
(xcb:+request exwm-systemtray--connection
(make-instance 'xcb:GrabKey
:owner-events 0
:grab-window icon
:modifiers xcb:ModMask:Any
:key xcb:Grab:Any
:pointer-mode xcb:GrabMode:Async
:keyboard-mode xcb:GrabMode:Async)))
(setq visible (slot-value info 'flags))
(if visible
(setq visible
(/= 0 (logand (slot-value info 'flags) xcb:xembed:MAPPED)))
;; Default to visible.
(setq visible t))
(when visible
(exwm--log "Map the window")
(xcb:+request exwm-systemtray--connection
(make-instance 'xcb:MapWindow :window icon)))
(xcb:+request exwm-systemtray--connection
(make-instance 'xcb:xembed:SendEvent
:destination icon
:event
(xcb:marshal
(make-instance 'xcb:xembed:EMBEDDED-NOTIFY
:window icon
:time xcb:Time:CurrentTime
:embedder
exwm-systemtray--embedder-window
:version 0)
exwm-systemtray--connection)))
(push `(,icon . ,(make-instance 'exwm-systemtray--icon
:width width*
:height height*
:visible visible))
exwm-systemtray--list)
(exwm-systemtray--refresh))))
(defun exwm-systemtray--unembed (icon)
"Unembed an icon."
(exwm--log "Unembed #x%x" icon)
(xcb:+request exwm-systemtray--connection
(make-instance 'xcb:UnmapWindow :window icon))
(xcb:+request exwm-systemtray--connection
(make-instance 'xcb:ReparentWindow
:window icon
:parent exwm--root
:x 0 :y 0))
(setq exwm-systemtray--list
(assq-delete-all icon exwm-systemtray--list))
(exwm-systemtray--refresh))
(defun exwm-systemtray--refresh ()
"Refresh the system tray."
(exwm--log)
;; Make sure to redraw the embedder.
(xcb:+request exwm-systemtray--connection
(make-instance 'xcb:UnmapWindow
:window exwm-systemtray--embedder-window))
(let ((x exwm-systemtray-icon-gap)
map)
(dolist (pair exwm-systemtray--list)
(when (slot-value (cdr pair) 'visible)
(xcb:+request exwm-systemtray--connection
(make-instance 'xcb:ConfigureWindow
:window (car pair)
:value-mask xcb:ConfigWindow:X
:x x))
(setq x (+ x (slot-value (cdr pair) 'width)
exwm-systemtray-icon-gap))
(setq map t)))
(let ((workarea (elt exwm-workspace--workareas
exwm-workspace-current-index)))
(xcb:+request exwm-systemtray--connection
(make-instance 'xcb:ConfigureWindow
:window exwm-systemtray--embedder-window
:value-mask (logior xcb:ConfigWindow:X
xcb:ConfigWindow:Width)
:x (- (aref workarea 2) x)
:width x)))
(when map
(xcb:+request exwm-systemtray--connection
(make-instance 'xcb:MapWindow
:window exwm-systemtray--embedder-window))))
(xcb:flush exwm-systemtray--connection))
(defun exwm-systemtray--on-DestroyNotify (data _synthetic)
"Unembed icons on DestroyNotify."
(exwm--log)
(let ((obj (make-instance 'xcb:DestroyNotify)))
(xcb:unmarshal obj data)
(with-slots (window) obj
(when (assoc window exwm-systemtray--list)
(exwm-systemtray--unembed window)))))
(defun exwm-systemtray--on-ReparentNotify (data _synthetic)
"Unembed icons on ReparentNotify."
(exwm--log)
(let ((obj (make-instance 'xcb:ReparentNotify)))
(xcb:unmarshal obj data)
(with-slots (window parent) obj
(when (and (/= parent exwm-systemtray--embedder-window)
(assoc window exwm-systemtray--list))
(exwm-systemtray--unembed window)))))
(defun exwm-systemtray--on-ResizeRequest (data _synthetic)
"Resize the tray icon on ResizeRequest."
(exwm--log)
(let ((obj (make-instance 'xcb:ResizeRequest))
attr)
(xcb:unmarshal obj data)
(with-slots (window width height) obj
(when (setq attr (cdr (assoc window exwm-systemtray--list)))
(with-slots ((width* width)
(height* height))
attr
(setq height* exwm-systemtray-height
width* (round (* width (/ (float height*) height))))
(when (< width* exwm-systemtray--icon-min-size)
(setq width* exwm-systemtray--icon-min-size
height* (round (* height (/ (float width*) width)))))
(xcb:+request exwm-systemtray--connection
(make-instance 'xcb:ConfigureWindow
:window window
:value-mask (logior xcb:ConfigWindow:Y
xcb:ConfigWindow:Width
xcb:ConfigWindow:Height)
;; Vertically centered.
:y (/ (- exwm-systemtray-height height*) 2)
:width width*
:height height*)))
(exwm-systemtray--refresh)))))
(defun exwm-systemtray--on-PropertyNotify (data _synthetic)
"Map/Unmap the tray icon on PropertyNotify."
(exwm--log)
(let ((obj (make-instance 'xcb:PropertyNotify))
attr info visible)
(xcb:unmarshal obj data)
(with-slots (window atom state) obj
(when (and (eq state xcb:Property:NewValue)
(eq atom xcb:Atom:_XEMBED_INFO)
(setq attr (cdr (assoc window exwm-systemtray--list))))
(setq info (xcb:+request-unchecked+reply exwm-systemtray--connection
(make-instance 'xcb:xembed:get-_XEMBED_INFO
:window window)))
(when info
(setq visible (/= 0 (logand (slot-value info 'flags)
xcb:xembed:MAPPED)))
(exwm--log "#x%x visible? %s" window visible)
(if visible
(xcb:+request exwm-systemtray--connection
(make-instance 'xcb:MapWindow :window window))
(xcb:+request exwm-systemtray--connection
(make-instance 'xcb:UnmapWindow :window window)))
(setf (slot-value attr 'visible) visible)
(exwm-systemtray--refresh))))))
(defun exwm-systemtray--on-ClientMessage (data _synthetic)
"Handle client messages."
(let ((obj (make-instance 'xcb:ClientMessage))
opcode data32)
(xcb:unmarshal obj data)
(with-slots (window type data) obj
(when (eq type xcb:Atom:_NET_SYSTEM_TRAY_OPCODE)
(setq data32 (slot-value data 'data32)
opcode (elt data32 1))
(exwm--log "opcode: %s" opcode)
(cond ((= opcode xcb:systemtray:opcode:REQUEST-DOCK)
(unless (assoc (elt data32 2) exwm-systemtray--list)
(exwm-systemtray--embed (elt data32 2))))
;; Not implemented (rarely used nowadays).
((or (= opcode xcb:systemtray:opcode:BEGIN-MESSAGE)
(= opcode xcb:systemtray:opcode:CANCEL-MESSAGE)))
(t
(exwm--log "Unknown opcode message: %s" obj)))))))
(defun exwm-systemtray--on-KeyPress (data _synthetic)
"Forward all KeyPress events to Emacs frame."
(exwm--log)
;; This function is only executed when there's no autohide minibuffer,
;; a workspace frame has the input focus and the pointer is over a
;; tray icon.
(let ((dest (frame-parameter (selected-frame) 'exwm-outer-id))
(obj (make-instance 'xcb:KeyPress)))
(xcb:unmarshal obj data)
(setf (slot-value obj 'event) dest)
(xcb:+request exwm-systemtray--connection
(make-instance 'xcb:SendEvent
:propagate 0
:destination dest
:event-mask xcb:EventMask:NoEvent
:event (xcb:marshal obj exwm-systemtray--connection))))
(xcb:flush exwm-systemtray--connection))
(defun exwm-systemtray--on-workspace-switch ()
"Reparent/Refresh the system tray in `exwm-workspace-switch-hook'."
(exwm--log)
(unless (exwm-workspace--minibuffer-own-frame-p)
(exwm-workspace--update-offsets)
(xcb:+request exwm-systemtray--connection
(make-instance 'xcb:ReparentWindow
:window exwm-systemtray--embedder-window
:parent (string-to-number
(frame-parameter exwm-workspace--current
'window-id))
:x 0
:y (- (elt (elt exwm-workspace--workareas
exwm-workspace-current-index)
3)
exwm-workspace--frame-y-offset
exwm-systemtray-height))))
(exwm-systemtray--refresh))
(defun exwm-systemtray--refresh-all ()
"Reposition/Refresh the system tray."
(exwm--log)
(unless (exwm-workspace--minibuffer-own-frame-p)
(exwm-workspace--update-offsets)
(xcb:+request exwm-systemtray--connection
(make-instance 'xcb:ConfigureWindow
:window exwm-systemtray--embedder-window
:value-mask xcb:ConfigWindow:Y
:y (- (elt (elt exwm-workspace--workareas
exwm-workspace-current-index)
3)
exwm-workspace--frame-y-offset
exwm-systemtray-height))))
(exwm-systemtray--refresh))
(cl-defun exwm-systemtray--init ()
"Initialize system tray module."
(exwm--log)
(cl-assert (not exwm-systemtray--connection))
(cl-assert (not exwm-systemtray--list))
(cl-assert (not exwm-systemtray--selection-owner-window))
(cl-assert (not exwm-systemtray--embedder-window))
(unless exwm-systemtray-height
(setq exwm-systemtray-height (max exwm-systemtray--icon-min-size
(line-pixel-height))))
;; Create a new connection.
(setq exwm-systemtray--connection (xcb:connect))
(set-process-query-on-exit-flag (slot-value exwm-systemtray--connection
'process)
nil)
;; Initialize XELB modules.
(xcb:xembed:init exwm-systemtray--connection t)
(xcb:systemtray:init exwm-systemtray--connection t)
;; Acquire the manager selection _NET_SYSTEM_TRAY_S0.
(with-slots (owner)
(xcb:+request-unchecked+reply exwm-systemtray--connection
(make-instance 'xcb:GetSelectionOwner
:selection xcb:Atom:_NET_SYSTEM_TRAY_S0))
(when (/= owner xcb:Window:None)
(xcb:disconnect exwm-systemtray--connection)
(setq exwm-systemtray--connection nil)
(warn "[EXWM] Other system tray detected")
(cl-return-from exwm-systemtray--init)))
(let ((id (xcb:generate-id exwm-systemtray--connection)))
(setq exwm-systemtray--selection-owner-window id)
(xcb:+request exwm-systemtray--connection
(make-instance 'xcb:CreateWindow
:depth 0
:wid id
:parent exwm--root
:x 0
:y 0
:width 1
:height 1
:border-width 0
:class xcb:WindowClass:InputOnly
:visual 0
:value-mask xcb:CW:OverrideRedirect
:override-redirect 1))
;; Get the selection ownership.
(xcb:+request exwm-systemtray--connection
(make-instance 'xcb:SetSelectionOwner
:owner id
:selection xcb:Atom:_NET_SYSTEM_TRAY_S0
:time xcb:Time:CurrentTime))
;; Send a client message to announce the selection.
(xcb:+request exwm-systemtray--connection
(make-instance 'xcb:SendEvent
:propagate 0
:destination exwm--root
:event-mask xcb:EventMask:StructureNotify
:event (xcb:marshal
(make-instance 'xcb:systemtray:-ClientMessage
:window exwm--root
:time xcb:Time:CurrentTime
:selection
xcb:Atom:_NET_SYSTEM_TRAY_S0
:owner id)
exwm-systemtray--connection)))
;; Set _NET_WM_NAME.
(xcb:+request exwm-systemtray--connection
(make-instance 'xcb:ewmh:set-_NET_WM_NAME
:window id
:data "EXWM: exwm-systemtray--selection-owner-window"))
;; Set the _NET_SYSTEM_TRAY_ORIENTATION property.
(xcb:+request exwm-systemtray--connection
(make-instance 'xcb:xembed:set-_NET_SYSTEM_TRAY_ORIENTATION
:window id
:data xcb:systemtray:ORIENTATION:HORZ)))
;; Create the embedder.
(let ((id (xcb:generate-id exwm-systemtray--connection))
(background-pixel (exwm--color->pixel exwm-systemtray-background-color))
frame parent depth y)
(setq exwm-systemtray--embedder-window id)
(if (exwm-workspace--minibuffer-own-frame-p)
(setq frame exwm-workspace--minibuffer
y (if (>= (line-pixel-height) exwm-systemtray-height)
;; Bottom aligned.
(- (line-pixel-height) exwm-systemtray-height)
;; Vertically centered.
(/ (- (line-pixel-height) exwm-systemtray-height) 2)))
(exwm-workspace--update-offsets)
(setq frame exwm-workspace--current
;; Bottom aligned.
y (- (elt (elt exwm-workspace--workareas
exwm-workspace-current-index)
3)
exwm-workspace--frame-y-offset
exwm-systemtray-height)))
(setq parent (string-to-number (frame-parameter frame 'window-id))
depth (slot-value (xcb:+request-unchecked+reply
exwm-systemtray--connection
(make-instance 'xcb:GetGeometry
:drawable parent))
'depth))
(xcb:+request exwm-systemtray--connection
(make-instance 'xcb:CreateWindow
:depth depth
:wid id
:parent parent
:x 0
:y y
:width 1
:height exwm-systemtray-height
:border-width 0
:class xcb:WindowClass:InputOutput
:visual 0
:value-mask (logior xcb:CW:BackPixmap
(if background-pixel
xcb:CW:BackPixel 0)
xcb:CW:EventMask)
:background-pixmap xcb:BackPixmap:ParentRelative
:background-pixel background-pixel
:event-mask xcb:EventMask:SubstructureNotify))
;; Set _NET_WM_NAME.
(xcb:+request exwm-systemtray--connection
(make-instance 'xcb:ewmh:set-_NET_WM_NAME
:window id
:data "EXWM: exwm-systemtray--embedder-window")))
(xcb:flush exwm-systemtray--connection)
;; Attach event listeners.
(xcb:+event exwm-systemtray--connection 'xcb:DestroyNotify
#'exwm-systemtray--on-DestroyNotify)
(xcb:+event exwm-systemtray--connection 'xcb:ReparentNotify
#'exwm-systemtray--on-ReparentNotify)
(xcb:+event exwm-systemtray--connection 'xcb:ResizeRequest
#'exwm-systemtray--on-ResizeRequest)
(xcb:+event exwm-systemtray--connection 'xcb:PropertyNotify
#'exwm-systemtray--on-PropertyNotify)
(xcb:+event exwm-systemtray--connection 'xcb:ClientMessage
#'exwm-systemtray--on-ClientMessage)
(unless (exwm-workspace--minibuffer-own-frame-p)
(xcb:+event exwm-systemtray--connection 'xcb:KeyPress
#'exwm-systemtray--on-KeyPress))
;; Add hook to move/reparent the embedder.
(add-hook 'exwm-workspace-switch-hook #'exwm-systemtray--on-workspace-switch)
(add-hook 'exwm-workspace--update-workareas-hook
#'exwm-systemtray--refresh-all)
(add-hook 'menu-bar-mode-hook #'exwm-systemtray--refresh-all)
(add-hook 'tool-bar-mode-hook #'exwm-systemtray--refresh-all)
(when (boundp 'exwm-randr-refresh-hook)
(add-hook 'exwm-randr-refresh-hook #'exwm-systemtray--refresh-all))
;; The struts can be updated already.
(when exwm-workspace--workareas
(exwm-systemtray--refresh-all)))
(defun exwm-systemtray--exit ()
"Exit the systemtray module."
(exwm--log)
(when exwm-systemtray--connection
;; Hide & reparent out the embedder before disconnection to prevent
;; embedded icons from being reparented to an Emacs frame (which is the
;; parent of the embedder).
(xcb:+request exwm-systemtray--connection
(make-instance 'xcb:UnmapWindow
:window exwm-systemtray--embedder-window))
(xcb:+request exwm-systemtray--connection
(make-instance 'xcb:ReparentWindow
:window exwm-systemtray--embedder-window
:parent exwm--root
:x 0
:y 0))
(xcb:disconnect exwm-systemtray--connection)
(setq exwm-systemtray--connection nil
exwm-systemtray--list nil
exwm-systemtray--selection-owner-window nil
exwm-systemtray--embedder-window nil)
(remove-hook 'exwm-workspace-switch-hook
#'exwm-systemtray--on-workspace-switch)
(remove-hook 'exwm-workspace--update-workareas-hook
#'exwm-systemtray--refresh-all)
(remove-hook 'menu-bar-mode-hook #'exwm-systemtray--refresh-all)
(remove-hook 'tool-bar-mode-hook #'exwm-systemtray--refresh-all)
(when (boundp 'exwm-randr-refresh-hook)
(remove-hook 'exwm-randr-refresh-hook #'exwm-systemtray--refresh-all))))
(defun exwm-systemtray-enable ()
"Enable system tray support for EXWM."
(exwm--log)
(add-hook 'exwm-init-hook #'exwm-systemtray--init)
(add-hook 'exwm-exit-hook #'exwm-systemtray--exit))
(provide 'exwm-systemtray)
;;; exwm-systemtray.el ends here

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@ -1,800 +0,0 @@
;;; exwm-xim.el --- XIM Module for EXWM -*- lexical-binding: t -*-
;; Copyright (C) 2019-2020 Free Software Foundation, Inc.
;; Author: Chris Feng <chris.w.feng@gmail.com>
;; This file is part of GNU Emacs.
;; GNU Emacs 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 3 of the License, or
;; (at your option) any later version.
;; GNU Emacs 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 GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
;;; Commentary:
;; This module adds XIM support for EXWM and allows sending characters
;; generated by any Emacs's builtin input method (info node `Input Methods')
;; to X windows.
;; This module is essentially an X input method server utilizing Emacs as
;; its backend. It talks with X windows through the XIM protocol. The XIM
;; protocol is quite flexible by itself, stating that an implementation can
;; create network connections of various types as well as make use of an
;; existing X connection for communication, and that an IM server may
;; support multiple transport versions, various input styles and several
;; event flow modals, etc. Here we only make choices that are most popular
;; among other IM servers and more importantly, practical for Emacs to act
;; as an IM server:
;;
;; + Packets are transported on top of an X connection like most IMEs.
;; + Only transport version 0.0 (i.e. only-CM & Property-with-CM) is
;; supported (same as "IM Server Developers Kit", adopted by most IMEs).
;; + Only support static event flow, on-demand-synchronous method.
;; + Only "root-window" input style is supported.
;; To use this module, first load and enable it as follows:
;;
;; (require 'exwm-xim)
;; (exwm-xim-enable)
;;
;; A keybinding for `toggle-input-method' is probably required to turn on &
;; off an input method (default to `default-input-method'). It's bound to
;; 'C-\' by default and can be made reachable when working with X windows:
;;
;; (push ?\C-\\ exwm-input-prefix-keys)
;;
;; It's also required (and error-prone) to setup environment variables to
;; make applications actually use this input method. Typically the
;; following lines should be inserted into '~/.xinitrc'.
;;
;; export XMODIFIERS=@im=exwm-xim
;; export GTK_IM_MODULE=xim
;; export QT_IM_MODULE=xim
;; export CLUTTER_IM_MODULE=xim
;; References:
;; + XIM (http://www.x.org/releases/X11R7.6/doc/libX11/specs/XIM/xim.html)
;; + IMdkit (http://xorg.freedesktop.org/archive/unsupported/lib/IMdkit/)
;; + UIM (https://github.com/uim/uim)
;;; Code:
(eval-when-compile (require 'cl-lib))
(require 'xcb-keysyms)
(require 'xcb-xim)
(require 'exwm-core)
(require 'exwm-input)
(defconst exwm-xim--locales
"@locale=\
aa,af,ak,am,an,anp,ar,as,ast,ayc,az,be,bem,ber,bg,bhb,bho,bn,bo,br,brx,bs,byn,\
ca,ce,cmn,crh,cs,csb,cv,cy,da,de,doi,dv,dz,el,en,es,et,eu,fa,ff,fi,fil,fo,fr,\
fur,fy,ga,gd,gez,gl,gu,gv,ha,hak,he,hi,hne,hr,hsb,ht,hu,hy,ia,id,ig,ik,is,it,\
iu,iw,ja,ka,kk,kl,km,kn,ko,kok,ks,ku,kw,ky,lb,lg,li,li,lij,lo,lt,lv,lzh,mag,\
mai,mg,mhr,mi,mk,ml,mn,mni,mr,ms,mt,my,nan,nb,nds,ne,nhn,niu,nl,nn,nr,nso,oc,\
om,or,os,pa,pa,pap,pl,ps,pt,quz,raj,ro,ru,rw,sa,sat,sc,sd,se,shs,si,sid,sk,sl,\
so,sq,sr,ss,st,sv,sw,szl,ta,tcy,te,tg,th,the,ti,tig,tk,tl,tn,tr,ts,tt,ug,uk,\
unm,ur,uz,ve,vi,wa,wae,wal,wo,xh,yi,yo,yue,zh,zu,\
C,no"
"All supported locales (stolen from glibc).")
(defconst exwm-xim--default-error
(make-instance 'xim:error
:im-id 0
:ic-id 0
:flag xim:error-flag:invalid-both
:error-code xim:error-code:bad-something
:length 0
:type 0
:detail nil)
"Default error returned to clients.")
(defconst exwm-xim--default-im-attrs
(list (make-instance 'xim:XIMATTR
:id 0
:type xim:ATTRIBUTE-VALUE-TYPE:xim-styles
:length (length xlib:XNQueryInputStyle)
:attribute xlib:XNQueryInputStyle))
"Default IM attrs returned to clients.")
(defconst exwm-xim--default-ic-attrs
(list (make-instance 'xim:XICATTR
:id 0
:type xim:ATTRIBUTE-VALUE-TYPE:long-data
:length (length xlib:XNInputStyle)
:attribute xlib:XNInputStyle)
(make-instance 'xim:XICATTR
:id 1
:type xim:ATTRIBUTE-VALUE-TYPE:window
:length (length xlib:XNClientWindow)
:attribute xlib:XNClientWindow)
;; Required by e.g. xterm.
(make-instance 'xim:XICATTR
:id 2
:type xim:ATTRIBUTE-VALUE-TYPE:window
:length (length xlib:XNFocusWindow)
:attribute xlib:XNFocusWindow))
"Default IC attrs returned to clients.")
(defconst exwm-xim--default-styles
(make-instance 'xim:XIMStyles
:number nil
:styles (list (logior xlib:XIMPreeditNothing
xlib:XIMStatusNothing)))
"Default styles: root-window, i.e. no preediting or status display support.")
(defconst exwm-xim--default-attributes
(list (make-instance 'xim:XIMATTRIBUTE
:id 0
:length nil
:value exwm-xim--default-styles))
"Default IM/IC attributes returned to clients.")
(defvar exwm-xim--conn nil
"The X connection for initiating other XIM connections.")
(defvar exwm-xim--event-xwin nil
"X window for initiating new XIM connections.")
(defvar exwm-xim--server-client-plist '(nil nil)
"Plist mapping server window to [X connection, client window, byte-order].")
(defvar exwm-xim--client-server-plist '(nil nil)
"Plist mapping client window to server window.")
(defvar exwm-xim--property-index 0 "For generating a unique property name.")
(defvar exwm-xim--im-id 0 "Last IM ID.")
(defvar exwm-xim--ic-id 0 "Last IC ID.")
;; X11 atoms.
(defvar exwm-xim--@server nil)
(defvar exwm-xim--LOCALES nil)
(defvar exwm-xim--TRANSPORT nil)
(defvar exwm-xim--XIM_SERVERS nil)
(defvar exwm-xim--_XIM_PROTOCOL nil)
(defvar exwm-xim--_XIM_XCONNECT nil)
(defun exwm-xim--on-SelectionRequest (data _synthetic)
"Handle SelectionRequest events on IMS window.
Such events would be received when clients query for LOCALES or TRANSPORT."
(exwm--log)
(let ((evt (make-instance 'xcb:SelectionRequest))
value fake-event)
(xcb:unmarshal evt data)
(with-slots (time requestor selection target property) evt
(setq value (cond ((= target exwm-xim--LOCALES)
;; Return supported locales.
exwm-xim--locales)
((= target exwm-xim--TRANSPORT)
;; Use XIM over an X connection.
"@transport=X/")))
(when value
;; Change the property.
(xcb:+request exwm-xim--conn
(make-instance 'xcb:ChangeProperty
:mode xcb:PropMode:Replace
:window requestor
:property property
:type target
:format 8
:data-len (length value)
:data value))
;; Send a SelectionNotify event.
(setq fake-event (make-instance 'xcb:SelectionNotify
:time time
:requestor requestor
:selection selection
:target target
:property property))
(xcb:+request exwm-xim--conn
(make-instance 'xcb:SendEvent
:propagate 0
:destination requestor
:event-mask xcb:EventMask:NoEvent
:event (xcb:marshal fake-event exwm-xim--conn)))
(xcb:flush exwm-xim--conn)))))
(cl-defun exwm-xim--on-ClientMessage-0 (data _synthetic)
"Handle ClientMessage event on IMS window (new connection).
Such events would be received when clients request for _XIM_XCONNECT.
A new X connection and server window would be created to communicate with
this client."
(exwm--log)
(let ((evt (make-instance 'xcb:ClientMessage))
conn client-xwin server-xwin)
(xcb:unmarshal evt data)
(with-slots (window type data) evt
(unless (= type exwm-xim--_XIM_XCONNECT)
;; Only handle _XIM_XCONNECT.
(exwm--log "Ignore ClientMessage %s" type)
(cl-return-from exwm-xim--on-ClientMessage-0))
(setq client-xwin (elt (slot-value data 'data32) 0)
;; Create a new X connection and a new server window.
conn (xcb:connect)
server-xwin (xcb:generate-id conn))
(set-process-query-on-exit-flag (slot-value conn 'process) nil)
;; Store this client.
(plist-put exwm-xim--server-client-plist server-xwin
`[,conn ,client-xwin nil])
(plist-put exwm-xim--client-server-plist client-xwin server-xwin)
;; Select DestroyNotify events on this client window.
(xcb:+request exwm-xim--conn
(make-instance 'xcb:ChangeWindowAttributes
:window client-xwin
:value-mask xcb:CW:EventMask
:event-mask xcb:EventMask:StructureNotify))
(xcb:flush exwm-xim--conn)
;; Handle ClientMessage events from this new connection.
(xcb:+event conn 'xcb:ClientMessage #'exwm-xim--on-ClientMessage)
;; Create a communication window.
(xcb:+request conn
(make-instance 'xcb:CreateWindow
:depth 0
:wid server-xwin
:parent exwm--root
:x 0
:y 0
:width 1
:height 1
:border-width 0
:class xcb:WindowClass:InputOutput
:visual 0
:value-mask xcb:CW:OverrideRedirect
:override-redirect 1))
(xcb:flush conn)
;; Send connection establishment ClientMessage.
(setf window client-xwin
(slot-value data 'data32) `(,server-xwin 0 0 0 0))
(slot-makeunbound data 'data8)
(slot-makeunbound data 'data16)
(xcb:+request exwm-xim--conn
(make-instance 'xcb:SendEvent
:propagate 0
:destination client-xwin
:event-mask xcb:EventMask:NoEvent
:event (xcb:marshal evt exwm-xim--conn)))
(xcb:flush exwm-xim--conn))))
(cl-defun exwm-xim--on-ClientMessage (data _synthetic)
"Handle ClientMessage event on IMS communication window (request).
Such events would be received when clients request for _XIM_PROTOCOL.
The actual XIM request is in client message data or a property."
(exwm--log)
(let ((evt (make-instance 'xcb:ClientMessage))
conn client-xwin server-xwin)
(xcb:unmarshal evt data)
(with-slots (format window type data) evt
(unless (= type exwm-xim--_XIM_PROTOCOL)
(exwm--log "Ignore ClientMessage %s" type)
(cl-return-from exwm-xim--on-ClientMessage))
(setq server-xwin window
conn (plist-get exwm-xim--server-client-plist server-xwin)
client-xwin (elt conn 1)
conn (elt conn 0))
(cond ((= format 8)
;; Data.
(exwm-xim--on-request (vconcat (slot-value data 'data8))
conn client-xwin server-xwin))
((= format 32)
;; Atom.
(with-slots (data32) data
(with-slots (value)
(xcb:+request-unchecked+reply conn
(make-instance 'xcb:GetProperty
:delete 1
:window server-xwin
:property (elt data32 1)
:type xcb:GetPropertyType:Any
:long-offset 0
:long-length (elt data32 0)))
(when (> (length value) 0)
(exwm-xim--on-request value conn client-xwin
server-xwin)))))))))
(defun exwm-xim--on-request (data conn client-xwin server-xwin)
"Handle an XIM reuqest."
(exwm--log)
(let ((opcode (elt data 0))
;; Let-bind `xim:lsb' to make pack/unpack functions work correctly.
(xim:lsb (elt (plist-get exwm-xim--server-client-plist server-xwin) 2))
req replies)
(cond ((= opcode xim:opcode:error)
(exwm--log "ERROR: %s" data))
((= opcode xim:opcode:connect)
(exwm--log "CONNECT")
(setq xim:lsb (= (elt data 4) xim:connect-byte-order:lsb-first))
;; Store byte-order.
(setf (elt (plist-get exwm-xim--server-client-plist server-xwin) 2)
xim:lsb)
(setq req (make-instance 'xim:connect))
(xcb:unmarshal req data)
(if (and (= (slot-value req 'major-version) 1)
(= (slot-value req 'minor-version) 0)
;; Do not support authentication.
(= (slot-value req 'number) 0))
;; Accept the connection.
(push (make-instance 'xim:connect-reply) replies)
;; Deny it.
(push exwm-xim--default-error replies)))
((memq opcode (list xim:opcode:auth-required
xim:opcode:auth-reply
xim:opcode:auth-next
xim:opcode:auth-ng))
(exwm--log "AUTH: %d" opcode)
;; Deny any attempt to make authentication.
(push exwm-xim--default-error replies))
((= opcode xim:opcode:disconnect)
(exwm--log "DISCONNECT")
;; Gracefully disconnect from the client.
(exwm-xim--make-request (make-instance 'xim:disconnect-reply)
conn client-xwin)
;; Destroy the communication window & connection.
(xcb:+request conn
(make-instance 'xcb:DestroyWindow
:window server-xwin))
(xcb:disconnect conn)
;; Clean up cache.
(cl-remf exwm-xim--server-client-plist server-xwin)
(cl-remf exwm-xim--client-server-plist client-xwin))
((= opcode xim:opcode:open)
(exwm--log "OPEN")
;; Note: We make no check here.
(setq exwm-xim--im-id (if (< exwm-xim--im-id #xffff)
(1+ exwm-xim--im-id)
1))
(setq replies
(list
(make-instance 'xim:open-reply
:im-id exwm-xim--im-id
:im-attrs-length nil
:im-attrs exwm-xim--default-im-attrs
:ic-attrs-length nil
:ic-attrs exwm-xim--default-ic-attrs)
(make-instance 'xim:set-event-mask
:im-id exwm-xim--im-id
:ic-id 0
;; Static event flow.
:forward-event-mask xcb:EventMask:KeyPress
;; on-demand-synchronous method.
:synchronous-event-mask
xcb:EventMask:NoEvent))))
((= opcode xim:opcode:close)
(exwm--log "CLOSE")
(setq req (make-instance 'xim:close))
(xcb:unmarshal req data)
(push (make-instance 'xim:close-reply
:im-id (slot-value req 'im-id))
replies))
((= opcode xim:opcode:trigger-notify)
(exwm--log "TRIGGER-NOTIFY")
;; Only static event flow modal is supported.
(push exwm-xim--default-error replies))
((= opcode xim:opcode:encoding-negotiation)
(exwm--log "ENCODING-NEGOTIATION")
(setq req (make-instance 'xim:encoding-negotiation))
(xcb:unmarshal req data)
(let ((index (cl-position "COMPOUND_TEXT"
(mapcar (lambda (i) (slot-value i 'name))
(slot-value req 'names))
:test #'equal)))
(unless index
;; Fallback to portable character encoding (a subset of ASCII).
(setq index -1))
(push (make-instance 'xim:encoding-negotiation-reply
:im-id (slot-value req 'im-id)
:category
xim:encoding-negotiation-reply-category:name
:index index)
replies)))
((= opcode xim:opcode:query-extension)
(exwm--log "QUERY-EXTENSION")
(setq req (make-instance 'xim:query-extension))
(xcb:unmarshal req data)
(push (make-instance 'xim:query-extension-reply
:im-id (slot-value req 'im-id)
;; No extension support.
:length 0
:extensions nil)
replies))
((= opcode xim:opcode:set-im-values)
(exwm--log "SET-IM-VALUES")
;; There's only one possible input method attribute.
(setq req (make-instance 'xim:set-im-values))
(xcb:unmarshal req data)
(push (make-instance 'xim:set-im-values-reply
:im-id (slot-value req 'im-id))
replies))
((= opcode xim:opcode:get-im-values)
(exwm--log "GET-IM-VALUES")
(setq req (make-instance 'xim:get-im-values))
(let (im-attributes-id)
(xcb:unmarshal req data)
(setq im-attributes-id (slot-value req 'im-attributes-id))
(if (cl-notevery (lambda (i) (= i 0)) im-attributes-id)
;; Only support one IM attributes.
(push (make-instance 'xim:error
:im-id (slot-value req 'im-id)
:ic-id 0
:flag xim:error-flag:invalid-ic-id
:error-code xim:error-code:bad-something
:length 0
:type 0
:detail nil)
replies)
(push
(make-instance 'xim:get-im-values-reply
:im-id (slot-value req 'im-id)
:length nil
:im-attributes exwm-xim--default-attributes)
replies))))
((= opcode xim:opcode:create-ic)
(exwm--log "CREATE-IC")
(setq req (make-instance 'xim:create-ic))
(xcb:unmarshal req data)
;; Note: The ic-attributes slot is ignored.
(setq exwm-xim--ic-id (if (< exwm-xim--ic-id #xffff)
(1+ exwm-xim--ic-id)
1))
(push (make-instance 'xim:create-ic-reply
:im-id (slot-value req 'im-id)
:ic-id exwm-xim--ic-id)
replies))
((= opcode xim:opcode:destroy-ic)
(exwm--log "DESTROY-IC")
(setq req (make-instance 'xim:destroy-ic))
(xcb:unmarshal req data)
(push (make-instance 'xim:destroy-ic-reply
:im-id (slot-value req 'im-id)
:ic-id (slot-value req 'ic-id))
replies))
((= opcode xim:opcode:set-ic-values)
(exwm--log "SET-IC-VALUES")
(setq req (make-instance 'xim:set-ic-values))
(xcb:unmarshal req data)
;; We don't distinguish between input contexts.
(push (make-instance 'xim:set-ic-values-reply
:im-id (slot-value req 'im-id)
:ic-id (slot-value req 'ic-id))
replies))
((= opcode xim:opcode:get-ic-values)
(exwm--log "GET-IC-VALUES")
(setq req (make-instance 'xim:get-ic-values))
(xcb:unmarshal req data)
(push (make-instance 'xim:get-ic-values-reply
:im-id (slot-value req 'im-id)
:ic-id (slot-value req 'ic-id)
:length nil
:ic-attributes exwm-xim--default-attributes)
replies))
((= opcode xim:opcode:set-ic-focus)
(exwm--log "SET-IC-FOCUS")
;; All input contexts are the same.
)
((= opcode xim:opcode:unset-ic-focus)
(exwm--log "UNSET-IC-FOCUS")
;; All input contexts are the same.
)
((= opcode xim:opcode:forward-event)
(exwm--log "FORWARD-EVENT")
(setq req (make-instance 'xim:forward-event))
(xcb:unmarshal req data)
(exwm-xim--handle-forward-event-request req xim:lsb conn
client-xwin))
((= opcode xim:opcode:sync)
(exwm--log "SYNC")
(setq req (make-instance 'xim:sync))
(xcb:unmarshal req data)
(push (make-instance 'xim:sync-reply
:im-id (slot-value req 'im-id)
:ic-id (slot-value req 'ic-id))
replies))
((= opcode xim:opcode:sync-reply)
(exwm--log "SYNC-REPLY"))
((= opcode xim:opcode:reset-ic)
(exwm--log "RESET-IC")
;; No context-specific data saved.
(setq req (make-instance 'xim:reset-ic))
(xcb:unmarshal req data)
(push (make-instance 'xim:reset-ic-reply
:im-id (slot-value req 'im-id)
:ic-id (slot-value req 'ic-id)
:length 0
:string "")
replies))
((memq opcode (list xim:opcode:str-conversion-reply
xim:opcode:preedit-start-reply
xim:opcode:preedit-caret-reply))
(exwm--log "PREEDIT: %d" opcode)
;; No preedit support.
(push exwm-xim--default-error replies))
(t
(exwm--log "Bad protocol")
(push exwm-xim--default-error replies)))
;; Actually send the replies.
(when replies
(mapc (lambda (reply)
(exwm-xim--make-request reply conn client-xwin))
replies)
(xcb:flush conn))))
(defun exwm-xim--handle-forward-event-request (req lsb conn client-xwin)
(let ((im-func (with-current-buffer (window-buffer)
input-method-function))
key-event keysym keysyms event result)
;; Note: The flag slot is ignored.
;; Do conversion in client's byte-order.
(let ((xcb:lsb lsb))
(setq key-event (make-instance 'xcb:KeyPress))
(xcb:unmarshal key-event (slot-value req 'event)))
(with-slots (detail state) key-event
(setq keysym (xcb:keysyms:keycode->keysym exwm-xim--conn detail
state))
(when (/= (car keysym) 0)
(setq event (xcb:keysyms:keysym->event
exwm-xim--conn
(car keysym)
(logand state (lognot (cdr keysym)))))))
(while (or (slot-value req 'event) unread-command-events)
(unless (slot-value req 'event)
(setq event (pop unread-command-events))
;; Handle events in (t . EVENT) format.
(when (and (consp event)
(eq (car event) t))
(setq event (cdr event))))
(if (or (not im-func)
;; `list' is the default method.
(eq im-func #'list)
(not event)
;; Select only printable keys.
(not (integerp event)) (> #x20 event) (< #x7e event))
;; Either there is no active input method, or invalid key
;; is detected.
(with-slots ((raw-event event)
im-id ic-id serial-number)
req
(if raw-event
(setq event raw-event)
(setq keysyms (xcb:keysyms:event->keysyms exwm-xim--conn event))
(with-slots (detail state) key-event
(setf detail (xcb:keysyms:keysym->keycode exwm-xim--conn
(caar keysyms))
state (cdar keysyms)))
(setq event (let ((xcb:lsb lsb))
(xcb:marshal key-event conn))))
(when event
(exwm-xim--make-request
(make-instance 'xim:forward-event
:im-id im-id
:ic-id ic-id
:flag xim:commit-flag:synchronous
:serial-number serial-number
:event event)
conn client-xwin)))
(when (eq exwm--selected-input-mode 'char-mode)
;; Grab keyboard temporarily for char-mode.
(exwm-input--grab-keyboard))
(unwind-protect
(with-temp-buffer
;; Always show key strokes.
(let ((input-method-use-echo-area t)
(exwm-input-line-mode-passthrough t))
(setq result (funcall im-func event))
;; Clear echo area for the input method.
(message nil)
;; This also works for portable character encoding.
(setq result
(encode-coding-string (concat result)
'compound-text-with-extensions))
(exwm-xim--make-request
(make-instance 'xim:commit-x-lookup-chars
:im-id (slot-value req 'im-id)
:ic-id (slot-value req 'ic-id)
:flag (logior xim:commit-flag:synchronous
xim:commit-flag:x-lookup-chars)
:length (length result)
:string result)
conn client-xwin)))
(when (eq exwm--selected-input-mode 'char-mode)
(exwm-input--release-keyboard))))
(xcb:flush conn)
(setf event nil
(slot-value req 'event) nil))))
(defun exwm-xim--make-request (req conn client-xwin)
"Make an XIM request REQ via connection CONN.
CLIENT-XWIN would receive a ClientMessage event either telling the client
the request data or where to fetch the data."
(exwm--log)
(let ((data (xcb:marshal req))
property format client-message-data client-message)
(if (<= (length data) 20)
;; Send short requests directly with client messages.
(setq format 8
;; Pad to 20 bytes.
data (append data (make-list (- 20 (length data)) 0))
client-message-data (make-instance 'xcb:ClientMessageData
:data8 data))
;; Send long requests with properties.
(setq property (exwm--intern-atom (format "_EXWM_XIM_%x"
exwm-xim--property-index)))
(cl-incf exwm-xim--property-index)
(xcb:+request conn
(make-instance 'xcb:ChangeProperty
:mode xcb:PropMode:Append
:window client-xwin
:property property
:type xcb:Atom:STRING
:format 8
:data-len (length data)
:data data))
;; Also send a client message to notify the client about this property.
(setq format 32
client-message-data (make-instance 'xcb:ClientMessageData
:data32 `(,(length data)
,property
;; Pad to 20 bytes.
0 0 0))))
;; Send the client message.
(setq client-message (make-instance 'xcb:ClientMessage
:format format
:window client-xwin
:type exwm-xim--_XIM_PROTOCOL
:data client-message-data))
(xcb:+request conn
(make-instance 'xcb:SendEvent
:propagate 0
:destination client-xwin
:event-mask xcb:EventMask:NoEvent
:event (xcb:marshal client-message conn)))))
(defun exwm-xim--on-DestroyNotify (data synthetic)
"Do cleanups on receiving DestroyNotify event.
Such event would be received when the client window is destroyed."
(exwm--log)
(unless synthetic
(let ((evt (make-instance 'xcb:DestroyNotify))
conn client-xwin server-xwin)
(xcb:unmarshal evt data)
(setq client-xwin (slot-value evt 'window)
server-xwin (plist-get exwm-xim--client-server-plist client-xwin))
(when server-xwin
(setq conn (aref (plist-get exwm-xim--server-client-plist server-xwin)
0))
(cl-remf exwm-xim--server-client-plist server-xwin)
(cl-remf exwm-xim--client-server-plist client-xwin)
;; Destroy the communication window & connection.
(xcb:+request conn
(make-instance 'xcb:DestroyWindow
:window server-xwin))
(xcb:disconnect conn)))))
(cl-defun exwm-xim--init ()
"Initialize the XIM module."
(exwm--log)
(when exwm-xim--conn
(cl-return-from exwm-xim--init))
;; Initialize atoms.
(setq exwm-xim--@server (exwm--intern-atom "@server=exwm-xim")
exwm-xim--LOCALES (exwm--intern-atom "LOCALES")
exwm-xim--TRANSPORT (exwm--intern-atom "TRANSPORT")
exwm-xim--XIM_SERVERS (exwm--intern-atom "XIM_SERVERS")
exwm-xim--_XIM_PROTOCOL (exwm--intern-atom "_XIM_PROTOCOL")
exwm-xim--_XIM_XCONNECT (exwm--intern-atom "_XIM_XCONNECT"))
;; Create a new connection and event window.
(setq exwm-xim--conn (xcb:connect)
exwm-xim--event-xwin (xcb:generate-id exwm-xim--conn))
(set-process-query-on-exit-flag (slot-value exwm-xim--conn 'process) nil)
;; Initialize xcb:keysyms module.
(xcb:keysyms:init exwm-xim--conn)
;; Listen to SelectionRequest event for connection establishment.
(xcb:+event exwm-xim--conn 'xcb:SelectionRequest
#'exwm-xim--on-SelectionRequest)
;; Listen to ClientMessage event on IMS window for new XIM connection.
(xcb:+event exwm-xim--conn 'xcb:ClientMessage #'exwm-xim--on-ClientMessage-0)
;; Listen to DestroyNotify event to do cleanups.
(xcb:+event exwm-xim--conn 'xcb:DestroyNotify #'exwm-xim--on-DestroyNotify)
;; Create the event window.
(xcb:+request exwm-xim--conn
(make-instance 'xcb:CreateWindow
:depth 0
:wid exwm-xim--event-xwin
:parent exwm--root
:x 0
:y 0
:width 1
:height 1
:border-width 0
:class xcb:WindowClass:InputOutput
:visual 0
:value-mask xcb:CW:OverrideRedirect
:override-redirect 1))
;; Set the selection owner.
(xcb:+request exwm-xim--conn
(make-instance 'xcb:SetSelectionOwner
:owner exwm-xim--event-xwin
:selection exwm-xim--@server
:time xcb:Time:CurrentTime))
;; Set XIM_SERVERS property on the root window.
(xcb:+request exwm-xim--conn
(make-instance 'xcb:ChangeProperty
:mode xcb:PropMode:Prepend
:window exwm--root
:property exwm-xim--XIM_SERVERS
:type xcb:Atom:ATOM
:format 32
:data-len 1
:data (funcall (if xcb:lsb
#'xcb:-pack-u4-lsb
#'xcb:-pack-u4)
exwm-xim--@server)))
(xcb:flush exwm-xim--conn))
(cl-defun exwm-xim--exit ()
"Exit the XIM module."
(exwm--log)
;; Close IMS communication connections.
(mapc (lambda (i)
(when (vectorp i)
(xcb:disconnect (elt i 0))))
exwm-xim--server-client-plist)
;; Close the IMS connection.
(unless exwm-xim--conn
(cl-return-from exwm-xim--exit))
;; Remove exwm-xim from XIM_SERVERS.
(let ((reply (xcb:+request-unchecked+reply exwm-xim--conn
(make-instance 'xcb:GetProperty
:delete 1
:window exwm--root
:property exwm-xim--XIM_SERVERS
:type xcb:Atom:ATOM
:long-offset 0
:long-length 1000)))
unpacked-reply pack unpack)
(unless reply
(cl-return-from exwm-xim--exit))
(setq reply (slot-value reply 'value))
(unless (> (length reply) 4)
(cl-return-from exwm-xim--exit))
(setq reply (vconcat reply)
pack (if xcb:lsb #'xcb:-pack-u4-lsb #'xcb:-pack-u4)
unpack (if xcb:lsb #'xcb:-unpack-u4-lsb #'xcb:-unpack-u4))
(dotimes (i (/ (length reply) 4))
(push (funcall unpack reply (* i 4)) unpacked-reply))
(setq unpacked-reply (delq exwm-xim--@server unpacked-reply)
reply (mapcar pack unpacked-reply))
(xcb:+request exwm-xim--conn
(make-instance 'xcb:ChangeProperty
:mode xcb:PropMode:Replace
:window exwm--root
:property exwm-xim--XIM_SERVERS
:type xcb:Atom:ATOM
:format 32
:data-len (length reply)
:data reply))
(xcb:flush exwm-xim--conn))
(xcb:disconnect exwm-xim--conn)
(setq exwm-xim--conn nil))
(defun exwm-xim-enable ()
"Enable XIM support for EXWM."
(exwm--log)
(add-hook 'exwm-init-hook #'exwm-xim--init)
(add-hook 'exwm-exit-hook #'exwm-xim--exit))
(provide 'exwm-xim)
;;; exwm-xim.el ends here

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@ -1,20 +0,0 @@
# Disable access control for the current user.
xhost +SI:localuser:$USER
# Make Java applications aware this is a non-reparenting window manager.
export _JAVA_AWT_WM_NONREPARENTING=1
# Set default cursor.
xsetroot -cursor_name left_ptr
# Set keyboard repeat rate.
xset r rate 200 60
# Uncomment the following block to use the exwm-xim module.
#export XMODIFIERS=@im=exwm-xim
#export GTK_IM_MODULE=xim
#export QT_IM_MODULE=xim
#export CLUTTER_IM_MODULE=xim
# Finally start Emacs
exec emacs

View File

@ -1,18 +0,0 @@
This is the file .../info/dir, which contains the
topmost node of the Info hierarchy, called (dir)Top.
The first time you invoke Info you start off looking at this node.

File: dir, Node: Top This is the top of the INFO tree
This (the Directory node) gives a menu of major topics.
Typing "q" exits, "H" lists all Info commands, "d" returns here,
"h" gives a primer for first-timers,
"mEmacs<Return>" visits the Emacs manual, etc.
In Emacs, you can click mouse button 2 on a menu item or cross reference
to select it.
* Menu:
Emacs
* jabber.el: (jabber). Emacs XMPP (Jabber) client

View File

@ -1,439 +0,0 @@
;;; jabber-activity.el --- show jabber activity in the mode line
;; Copyright (C) 2004 Carl Henrik Lunde - <chlunde+jabber+@ping.uio.no>
;; This file is a part of jabber.el
;; 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, or (at your option)
;; any later version.
;; GNU Emacs 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 GNU Emacs; see the file COPYING. If not, write to the
;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
;; Boston, MA 02111-1307, USA.
;;; Commentary:
;; Allows tracking messages from buddies using the global mode line
;; See (info "(jabber)Tracking activity")
;;; TODO:
;; - Make it possible to enable this mode using M-x customize
;; - When Emacs is on another desktop, (get-buffer-window buf 'visible)
;; returns nil. We need to know when the user selects the frame again
;; so we can remove the string from the mode line. (Or just run
;; jabber-activity-clean often).
;; - jabber-activity-switch-to needs a keybinding. In which map?
;; - Is there any need for having defcustom jabber-activity-make-string?
;; - When there's activity in a buffer it would be nice with a hook which
;; does the opposite of bury-buffer, so switch-to-buffer will show that
;; buffer first.
;;; Code:
(require 'jabber-core)
(require 'jabber-alert)
(require 'jabber-util)
(require 'jabber-muc-nick-completion) ;we need jabber-muc-looks-like-personal-p
(require 'cl)
(defgroup jabber-activity nil
"activity tracking options"
:group 'jabber)
;; All the (featurep 'jabber-activity) is so we don't call a function
;; with an autoloaded cookie while the file is loading, since that
;; would lead to endless load recursion.
(defcustom jabber-activity-make-string 'jabber-activity-make-string-default
"Function to call, for making the string to put in the mode
line. The default function returns the nick of the user."
:set #'(lambda (var val)
(custom-set-default var val)
(when (and (featurep 'jabber-activity)
(fboundp 'jabber-activity-make-name-alist))
(jabber-activity-make-name-alist)
(jabber-activity-mode-line-update)))
:type 'function
:group 'jabber-activity)
(defcustom jabber-activity-shorten-minimum 1
"All strings returned by `jabber-activity-make-strings-shorten' will be
at least this long, when possible."
:group 'jabber-activity
:type 'number)
(defcustom jabber-activity-make-strings 'jabber-activity-make-strings-default
"Function which should return an alist of JID -> string when given a list of
JIDs."
:set #'(lambda (var val)
(custom-set-default var val)
(when (and (featurep 'jabber-activity)
(fboundp 'jabber-activity-make-name-alist))
(jabber-activity-make-name-alist)
(jabber-activity-mode-line-update)))
:type '(choice (function-item :tag "Keep strings"
:value jabber-activity-make-strings-default)
(function-item :tag "Shorten strings"
:value jabber-activity-make-strings-shorten)
(function :tag "Other function"))
:group 'jabber-activity)
(defcustom jabber-activity-count-in-title nil
"If non-nil, display number of active JIDs in frame title."
:type 'boolean
:group 'jabber-activity
:set #'(lambda (var val)
(custom-set-default var val)
(when (and (featurep 'jabber-activity)
(bound-and-true-p jabber-activity-mode))
(jabber-activity-mode -1)
(jabber-activity-mode 1))))
(defcustom jabber-activity-count-in-title-format
'(jabber-activity-jids ("[" jabber-activity-count-string "] "))
"Format string used for displaying activity in frame titles.
Same syntax as `mode-line-format'."
:type 'sexp
:group 'jabber-activity
:set #'(lambda (var val)
(if (not (and (featurep 'jabber-activity) (bound-and-true-p jabber-activity-mode)))
(custom-set-default var val)
(jabber-activity-mode -1)
(custom-set-default var val)
(jabber-activity-mode 1))))
(defcustom jabber-activity-show-p 'jabber-activity-show-p-default
"Predicate function to call to check if the given JID should be
shown in the mode line or not."
:type 'function
:group 'jabber-activity)
(defcustom jabber-activity-query-unread t
"Query the user as to whether killing Emacs should be cancelled when
there are unread messages which otherwise would be lost."
:type 'boolean
:group 'jabber-activity)
(defcustom jabber-activity-banned nil
"List of regexps of banned JID"
:type '(repeat string)
:group 'jabber-activity)
(defface jabber-activity-face
'((t (:foreground "red" :weight bold)))
"The face for displaying jabber-activity-string in the mode line"
:group 'jabber-activity)
(defface jabber-activity-personal-face
'((t (:foreground "blue" :weight bold)))
"The face for displaying personal jabber-activity-string in the mode line"
:group 'jabber-activity)
(defvar jabber-activity-jids nil
"A list of JIDs which have caused activity")
(defvar jabber-activity-personal-jids nil
"Subset of `jabber-activity-jids' for JIDs with \"personal\" activity.")
(defvar jabber-activity-name-alist nil
"Alist of mode line names for bare JIDs")
(defvar jabber-activity-mode-string ""
"The mode string for jabber activity")
(defvar jabber-activity-count-string "0"
"Number of active JIDs as a string.")
(defvar jabber-activity-update-hook nil
"Hook called when `jabber-activity-jids' changes.
It is called after `jabber-activity-mode-string' and
`jabber-activity-count-string' are updated.")
;; Protect this variable from being set in Local variables etc.
(put 'jabber-activity-mode-string 'risky-local-variable t)
(put 'jabber-activity-count-string 'risky-local-variable t)
(defun jabber-activity-make-string-default (jid)
"Return the nick of the JID. If no nick is available, return
the user name part of the JID. In private MUC conversations,
return the user's nickname."
(if (jabber-muc-sender-p jid)
(jabber-jid-resource jid)
(let ((nick (jabber-jid-displayname jid))
(user (jabber-jid-user jid))
(username (jabber-jid-username jid)))
(if (and username (string= nick user))
username
nick))))
(defun jabber-activity-make-strings-default (jids)
"Apply `jabber-activity-make-string' on JIDS"
(mapcar #'(lambda (jid) (cons jid (funcall jabber-activity-make-string jid)))
jids))
(defun jabber-activity-common-prefix (s1 s2)
"Return length of common prefix string shared by S1 and S2"
(let ((len (min (length s1) (length s2))))
(or (dotimes (i len)
(when (not (eq (aref s1 i) (aref s2 i)))
(return i)))
;; Substrings, equal, nil, or empty ("")
len)))
(defun jabber-activity-make-strings-shorten (jids)
"Return an alist of JID -> names acquired by running
`jabber-activity-make-string' on JIDS, and then shortening the names
as much as possible such that all strings still are unique and at
least `jabber-activity-shorten-minimum' long."
(let ((alist
(sort (mapcar
#'(lambda (x) (cons x (funcall jabber-activity-make-string x)))
jids)
#'(lambda (x y) (string-lessp (cdr x) (cdr y))))))
(loop for ((prev-jid . prev) (cur-jid . cur) (next-jid . next))
on (cons nil alist)
until (null cur)
collect
(cons
cur-jid
(substring
cur
0 (min (length cur)
(max jabber-activity-shorten-minimum
(1+ (jabber-activity-common-prefix cur prev))
(1+ (jabber-activity-common-prefix cur next)))))))))
(defun jabber-activity-find-buffer-name (jid)
"Find the name of the buffer that messages from JID would use."
(or (and (jabber-jid-resource jid)
(get-buffer (jabber-muc-private-get-buffer
(jabber-jid-user jid)
(jabber-jid-resource jid))))
(get-buffer (jabber-chat-get-buffer jid))
(get-buffer (jabber-muc-get-buffer jid))))
(defun jabber-activity-show-p-default (jid)
"Returns t only if there is an invisible buffer for JID
and JID not in jabber-activity-banned"
(let ((buffer (jabber-activity-find-buffer-name jid)))
(and (buffer-live-p buffer)
(not (get-buffer-window buffer 'visible))
(not (dolist (entry jabber-activity-banned)
(when (string-match entry jid)
(return t)))))))
(defun jabber-activity-make-name-alist ()
"Rebuild `jabber-activity-name-alist' based on currently known JIDs"
(let ((jids (or (mapcar #'car jabber-activity-name-alist)
(mapcar #'symbol-name *jabber-roster*))))
(setq jabber-activity-name-alist
(funcall jabber-activity-make-strings jids))))
(defun jabber-activity-lookup-name (jid)
"Lookup name in `jabber-activity-name-alist', creates an entry
if needed, and returns a (jid . string) pair suitable for the mode line"
(let ((elm (assoc jid jabber-activity-name-alist)))
(if elm
elm
(progn
;; Remake alist with the new JID
(setq jabber-activity-name-alist
(funcall jabber-activity-make-strings
(cons jid (mapcar #'car jabber-activity-name-alist))))
(jabber-activity-lookup-name jid)))))
(defun jabber-activity-mode-line-update ()
"Update the string shown in the mode line using `jabber-activity-make-string'
on JIDs where `jabber-activity-show-p'. Optional not-nil GROUP mean that message come from MUC.
Optional TEXT used with one-to-one or MUC chats and may be used to identify personal MUC message.
Optional PRESENCE mean personal presence request or alert."
(setq jabber-activity-mode-string
(if jabber-activity-jids
(mapconcat
(lambda (x)
(let ((jump-to-jid (car x)))
(jabber-propertize
(cdr x)
'face (if (member jump-to-jid jabber-activity-personal-jids)
'jabber-activity-personal-face
'jabber-activity-face)
;; XXX: XEmacs doesn't have make-mode-line-mouse-map.
;; Is there another way to make this work?
'local-map (when (fboundp 'make-mode-line-mouse-map)
(make-mode-line-mouse-map
'mouse-1 `(lambda ()
(interactive "@")
(jabber-activity-switch-to
,(car x)))))
'help-echo (concat "Jump to "
(jabber-jid-displayname (car x))
"'s buffer"))))
(mapcar #'jabber-activity-lookup-name
jabber-activity-jids)
",")
""))
(setq jabber-activity-count-string
(number-to-string (length jabber-activity-jids)))
(force-mode-line-update 'all)
(run-hooks 'jabber-activity-update-hook))
;;; Hooks
(defun jabber-activity-clean ()
"Remove JIDs where `jabber-activity-show-p' no longer is true"
(setq jabber-activity-jids (delete-if-not jabber-activity-show-p
jabber-activity-jids))
(setq jabber-activity-personal-jids
(delete-if-not jabber-activity-show-p
jabber-activity-personal-jids))
(jabber-activity-mode-line-update))
(defun jabber-activity-add (from buffer text proposed-alert)
"Add a JID to mode line when `jabber-activity-show-p'"
(when (funcall jabber-activity-show-p from)
(add-to-list 'jabber-activity-jids from)
(add-to-list 'jabber-activity-personal-jids from)
(jabber-activity-mode-line-update)))
(defun jabber-activity-add-muc (nick group buffer text proposed-alert)
"Add a JID to mode line when `jabber-activity-show-p'"
(when (funcall jabber-activity-show-p group)
(add-to-list 'jabber-activity-jids group)
(when (jabber-muc-looks-like-personal-p text group)
(add-to-list 'jabber-activity-personal-jids group))
(jabber-activity-mode-line-update)))
(defun jabber-activity-presence (who oldstatus newstatus statustext proposed-alert)
"Add a JID to mode line on subscription requests."
(when (string= newstatus "subscribe")
(add-to-list 'jabber-activity-jids (symbol-name who))
(add-to-list 'jabber-activity-personal-jids (symbol-name who))
(jabber-activity-mode-line-update)))
(defun jabber-activity-kill-hook ()
"Query the user as to whether killing Emacs should be cancelled
when there are unread messages which otherwise would be lost, if
`jabber-activity-query-unread' is t"
(if (and jabber-activity-jids
jabber-activity-query-unread)
(or jabber-silent-mode (yes-or-no-p
"You have unread Jabber messages, are you sure you want to quit?"))
t))
;;; Interactive functions
(defvar jabber-activity-last-buffer nil
"Last non-Jabber buffer used.")
(defun jabber-activity-switch-to (&optional jid-param)
"If JID-PARAM is provided, switch to that buffer. If JID-PARAM is nil and
there has been activity in another buffer, switch to that buffer. If no such
buffer exists, switch back to the last non Jabber chat buffer used."
(interactive)
(if (or jid-param jabber-activity-jids)
(let ((jid (or jid-param (car jabber-activity-jids))))
(unless (eq major-mode 'jabber-chat-mode)
(setq jabber-activity-last-buffer (current-buffer)))
(switch-to-buffer (jabber-activity-find-buffer-name jid))
(jabber-activity-clean))
(if (eq major-mode 'jabber-chat-mode)
;; Switch back to the buffer used last
(when (buffer-live-p jabber-activity-last-buffer)
(switch-to-buffer jabber-activity-last-buffer))
(message "No new activity"))))
(defvar jabber-activity-idle-timer nil "Idle timer used for activity cleaning")
;;;###autoload
(define-minor-mode jabber-activity-mode
"Toggle display of activity in hidden jabber buffers in the mode line.
With a numeric arg, enable this display if arg is positive."
:global t
:group 'jabber-activity
:init-value t
(if jabber-activity-mode
(progn
;; XEmacs compatibilty hack from erc-track
(if (featurep 'xemacs)
(defadvice switch-to-buffer (after jabber-activity-update (&rest args) activate)
(jabber-activity-clean))
(add-hook 'window-configuration-change-hook
'jabber-activity-clean))
(add-hook 'jabber-message-hooks
'jabber-activity-add)
(add-hook 'jabber-muc-hooks
'jabber-activity-add-muc)
(add-hook 'jabber-presence-hooks
'jabber-activity-presence)
(setq jabber-activity-idle-timer (run-with-idle-timer 2 t 'jabber-activity-clean))
;; XXX: reactivate
;; (add-hook 'jabber-post-connect-hooks
;; 'jabber-activity-make-name-alist)
(add-to-list 'kill-emacs-query-functions
'jabber-activity-kill-hook)
(add-to-list 'global-mode-string
'(t jabber-activity-mode-string))
(when jabber-activity-count-in-title
;; Be careful not to override specific meanings of the
;; existing title format. In particular, if the car is
;; a symbol, we can't just add our stuff at the beginning.
;; If the car is "", we should be safe.
;;
;; In my experience, sometimes the activity count gets
;; included twice in the title. I'm not sure exactly why,
;; but it would be nice to replace the code below with
;; something cleaner.
(if (equal (car-safe frame-title-format) "")
(add-to-list 'frame-title-format
jabber-activity-count-in-title-format)
(setq frame-title-format (list ""
jabber-activity-count-in-title-format
frame-title-format)))
(if (equal (car-safe icon-title-format) "")
(add-to-list 'icon-title-format
jabber-activity-count-in-title-format)
(setq icon-title-format (list ""
jabber-activity-count-in-title-format
icon-title-format)))))
(progn
(if (featurep 'xemacs)
(ad-disable-advice 'switch-to-buffer 'after 'jabber-activity-update)
(remove-hook 'window-configuration-change-hook
'jabber-activity-remove-visible))
(remove-hook 'jabber-message-hooks
'jabber-activity-add)
(remove-hook 'jabber-muc-hooks
'jabber-activity-add-muc)
(remove-hook 'jabber-presence-hooks
'jabber-activity-presence)
(ignore-errors (cancel-timer jabber-activity-idle-timer))
;; XXX: reactivate
;; (remove-hook 'jabber-post-connect-hooks
;; 'jabber-activity-make-name-alist)
(setq global-mode-string (delete '(t jabber-activity-mode-string)
global-mode-string))
(when (listp frame-title-format)
(setq frame-title-format
(delete jabber-activity-count-in-title-format
frame-title-format)))
(when (listp icon-title-format)
(setq icon-title-format
(delete jabber-activity-count-in-title-format
icon-title-format))))))
;; XXX: define-minor-mode should probably do this for us, but it doesn't.
(if jabber-activity-mode (jabber-activity-mode 1))
(provide 'jabber-activity)
;; arch-tag: 127D7E42-356B-11D9-BE1E-000A95C2FCD0

View File

@ -1,107 +0,0 @@
;; jabber-ahc-presence.el - provide remote control of presence
;; Copyright (C) 2003, 2004, 2007, 2008 - Magnus Henoch - mange@freemail.hu
;; Copyright (C) 2002, 2003, 2004 - tom berger - object@intelectronica.net
;; This file is a part of jabber.el.
;; 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
(require 'jabber-ahc)
(defconst jabber-ahc-presence-node "http://jabber.org/protocol/rc#set-status"
"Node used by jabber-ahc-presence")
(jabber-ahc-add jabber-ahc-presence-node "Set presence" 'jabber-ahc-presence
'jabber-my-jid-p)
(defun jabber-ahc-presence (jc xml-data)
"Process presence change command."
(let* ((query (jabber-iq-query xml-data))
(sessionid (jabber-xml-get-attribute query 'sessionid))
(action (jabber-xml-get-attribute query 'action)))
;; No session state is kept; instead, lack of session-id is used
;; as indication of first command.
(cond
;; command cancelled
((string= action "cancel")
`(command ((xmlns . "http://jabber.org/protocol/commands")
(sessionid . ,sessionid)
(node . ,jabber-ahc-presence-node)
(status . "canceled"))))
;; return form
((null sessionid)
`(command ((xmlns . "http://jabber.org/protocol/commands")
(sessionid . "jabber-ahc-presence")
(node . ,jabber-ahc-presence-node)
(status . "executing"))
(x ((xmlns . "jabber:x:data")
(type . "form"))
(title nil ,(format "Set presence of %s" (jabber-connection-jid jc)))
(instructions nil "Select new presence status.")
(field ((var . "FORM_TYPE") (type . "hidden"))
(value nil "http://jabber.org/protocol/rc"))
(field ((var . "status")
(label . "Status")
(type . "list-single"))
(value nil ,(if (string= *jabber-current-show* "")
"online"
*jabber-current-show*))
(option ((label . "Online")) (value nil "online"))
(option ((label . "Chatty")) (value nil "chat"))
(option ((label . "Away")) (value nil "away"))
(option ((label . "Extended away")) (value nil "xa"))
(option ((label . "Do not disturb")) (value nil "dnd")))
(field ((var . "status-message")
(label . "Message")
(type . "text-single"))
(value nil ,*jabber-current-status*))
(field ((var . "status-priority")
(label . "Priority")
(type . "text-single"))
(value nil ,(int-to-string *jabber-current-priority*))))))
;; process form
(t
(let* ((x (car (jabber-xml-get-children query 'x)))
;; we assume that the first <x/> is the jabber:x:data one
(fields (jabber-xml-get-children x 'field))
(new-show *jabber-current-show*)
(new-status *jabber-current-status*)
(new-priority *jabber-current-priority*))
(dolist (field fields)
(let ((var (jabber-xml-get-attribute field 'var))
;; notice that multi-value fields won't be handled properly
;; by this
(value (car (jabber-xml-node-children (car (jabber-xml-get-children field 'value))))))
(cond
((string= var "status")
(setq new-show (if (string= value "online")
""
value)))
((string= var "status-message")
(setq new-status value))
((string= var "status-priority")
(setq new-priority (string-to-number value))))))
(jabber-send-presence new-show new-status new-priority))
`(command ((xmlns . "http://jabber.org/protocol/commands")
(sessionid . ,sessionid)
(node . ,jabber-ahc-presence-node)
(status . "completed"))
(note ((type . "info")) "Presence has been changed."))))))
(provide 'jabber-ahc-presence)
;;; arch-tag: 4b8cbbe7-00a9-4d42-a4ac-b824ab914fba

View File

@ -1,231 +0,0 @@
;; jabber-ahc.el - Ad-Hoc Commands by JEP-0050
;; Copyright (C) 2003, 2004, 2007, 2008 - Magnus Henoch - mange@freemail.hu
;; Copyright (C) 2002, 2003, 2004 - tom berger - object@intelectronica.net
;; This file is a part of jabber.el.
;; 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
(require 'jabber-disco)
(require 'jabber-widget)
(defvar jabber-ahc-sessionid nil
"session id of Ad-Hoc Command session")
(defvar jabber-ahc-node nil
"node to send commands to")
(defvar jabber-ahc-commands nil
"Commands provided
This is an alist, where the keys are node names as strings (which
means that they must not conflict). The values are plists having
following properties:
acl - function taking connection object and JID of requester,
returning non-nil for access allowed. No function means
open for everyone.
name - name of command
func - function taking connection object and entire IQ stanza as
arguments and returning a <command/> node
Use the function `jabber-ahc-add' to add a command to this list.")
;;; SERVER
(add-to-list 'jabber-disco-info-nodes
(list "http://jabber.org/protocol/commands"
'((identity ((category . "automation")
(type . "command-list")
(name . "Ad-Hoc Command list")))
(feature ((var . "http://jabber.org/protocol/commands")))
(feature ((var . "http://jabber.org/protocol/disco#items")))
(feature
((var . "http://jabber.org/protocol/disco#info"))))))
(defun jabber-ahc-add (node name func acl)
"Add a command to internal lists.
NODE is the node name to be used. It must be unique.
NAME is the natural-language name of the command.
FUNC is a function taking the entire IQ stanza as single argument when
this command is invoked, and returns a <command/> node.
ACL is a function taking JID as single argument, returning non-nil for
access allowed. nil means open for everyone."
(add-to-list 'jabber-ahc-commands (cons node (list 'name name
'func func
'acl acl)))
(add-to-list 'jabber-disco-info-nodes
(list node `((identity ((category . "automation")
(type . "command-node")
(name . ,name)))
(feature ((var . "http://jabber.org/protocol/commands")))
(feature ((var . "http://jabber.org/protocol/disco#info")))
(feature ((var . "jabber:x:data")))))))
(jabber-disco-advertise-feature "http://jabber.org/protocol/commands")
(add-to-list 'jabber-disco-items-nodes
(list "http://jabber.org/protocol/commands" #'jabber-ahc-disco-items nil))
(defun jabber-ahc-disco-items (jc xml-data)
"Return commands in response to disco#items request"
(let ((jid (jabber-xml-get-attribute xml-data 'from)))
(mapcar (function
(lambda (command)
(let ((node (car command))
(plist (cdr command)))
(let ((acl (plist-get plist 'acl))
(name (plist-get plist 'name))
(func (plist-get plist 'func)))
(when (or (not (functionp acl))
(funcall acl jc jid))
`(item ((name . ,name)
(jid . ,(jabber-connection-jid jc))
(node . ,node))))))))
jabber-ahc-commands)))
(add-to-list 'jabber-iq-set-xmlns-alist
(cons "http://jabber.org/protocol/commands" 'jabber-ahc-process))
(defun jabber-ahc-process (jc xml-data)
(let ((to (jabber-xml-get-attribute xml-data 'from))
(id (jabber-xml-get-attribute xml-data 'id))
(node (jabber-xml-get-attribute (jabber-iq-query xml-data) 'node)))
;; find command
(let* ((plist (cdr (assoc node jabber-ahc-commands)))
(acl (plist-get plist 'acl))
(func (plist-get plist 'func)))
(if plist
;; found
(if (or (not (functionp acl))
(funcall acl jc to))
;; access control passed
(jabber-send-iq jc to "result"
(funcall func jc xml-data)
nil nil nil nil id)
;; ...or failed
(jabber-signal-error "cancel" 'not-allowed))
;; No such node
(jabber-signal-error "cancel" 'item-not-found)))))
;;; CLIENT
(add-to-list 'jabber-jid-service-menu
(cons "Request command list" 'jabber-ahc-get-list))
(defun jabber-ahc-get-list (jc to)
"Request list of ad-hoc commands. (JEP-0050)"
(interactive (list (jabber-read-account)
(jabber-read-jid-completing "Request command list from: " nil nil nil 'full t)))
(jabber-get-disco-items jc to "http://jabber.org/protocol/commands"))
(add-to-list 'jabber-jid-service-menu
(cons "Execute command" 'jabber-ahc-execute-command))
(defun jabber-ahc-execute-command (jc to node)
"Execute ad-hoc command. (JEP-0050)"
(interactive (list (jabber-read-account)
(jabber-read-jid-completing "Execute command of: " nil nil nil 'full t)
(jabber-read-node "Node of command: ")))
(jabber-send-iq jc to
"set"
`(command ((xmlns . "http://jabber.org/protocol/commands")
(node . ,node)
(action . "execute")))
#'jabber-process-data #'jabber-ahc-display
#'jabber-process-data "Command execution failed"))
(defun jabber-ahc-display (jc xml-data)
(let* ((from (jabber-xml-get-attribute xml-data 'from))
(query (jabber-iq-query xml-data))
(node (jabber-xml-get-attribute query 'node))
(notes (jabber-xml-get-children query 'note))
(sessionid (jabber-xml-get-attribute query 'sessionid))
(status (jabber-xml-get-attribute query 'status))
(actions (car (jabber-xml-get-children query 'actions)))
xdata
(inhibit-read-only t))
(make-local-variable 'jabber-ahc-sessionid)
(setq jabber-ahc-sessionid sessionid)
(make-local-variable 'jabber-ahc-node)
(setq jabber-ahc-node node)
(make-local-variable 'jabber-buffer-connection)
(setq jabber-buffer-connection jc)
(dolist (x (jabber-xml-get-children query 'x))
(when (string= (jabber-xml-get-attribute x 'xmlns) "jabber:x:data")
(setq xdata x)))
(cond
((string= status "executing")
(insert "Executing command\n\n"))
((string= status "completed")
(insert "Command completed\n\n"))
((string= status "canceled")
(insert "Command canceled\n\n")))
(dolist (note notes)
(let ((note-type (jabber-xml-get-attribute note 'type)))
(cond
((string= note-type "warn")
(insert "Warning: "))
((string= note-type "error")
(insert "Error: ")))
(insert (car (jabber-xml-node-children note)) "\n")))
(insert "\n")
(when xdata
(jabber-init-widget-buffer from)
(let ((formtype (jabber-xml-get-attribute xdata 'type)))
(if (string= formtype "result")
(jabber-render-xdata-search-results xdata)
(jabber-render-xdata-form xdata)
(when (string= status "executing")
(let ((button-titles
(cond
((null actions)
'(complete cancel))
(t
(let ((children (mapcar #'jabber-xml-node-name (jabber-xml-node-children actions)))
(default-action (jabber-xml-get-attribute actions 'execute)))
(if (or (null default-action) (memq (intern default-action) children))
children
(cons (intern default-action) children)))))))
(dolist (button-title button-titles)
(widget-create 'push-button :notify `(lambda (&rest ignore) (jabber-ahc-submit (quote ,button-title))) (symbol-name button-title))
(widget-insert "\t")))
(widget-insert "\n"))))
(widget-setup)
(widget-minor-mode 1))))
(defun jabber-ahc-submit (action)
"Submit Ad-Hoc Command."
(jabber-send-iq jabber-buffer-connection jabber-submit-to
"set"
`(command ((xmlns . "http://jabber.org/protocol/commands")
(sessionid . ,jabber-ahc-sessionid)
(node . ,jabber-ahc-node)
(action . ,(symbol-name action)))
,(if (and (not (eq action 'cancel))
(eq jabber-form-type 'xdata))
(jabber-parse-xdata-form)))
#'jabber-process-data #'jabber-ahc-display
#'jabber-process-data "Command execution failed"))
(provide 'jabber-ahc)
;;; arch-tag: c0d5ed8c-50cb-44e1-8e0f-4058b79ee353

View File

@ -1,514 +0,0 @@
;; jabber-alert.el - alert hooks
;; Copyright (C) 2003, 2004, 2005, 2007, 2008 - Magnus Henoch - mange@freemail.hu
;; Copyright (C) 2002, 2003, 2004 - tom berger - object@intelectronica.net
;; This file is a part of jabber.el.
;; 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
(require 'jabber-util)
(require 'cl)
(defgroup jabber-alerts nil "auditory and visual alerts for jabber events"
:group 'jabber)
(defcustom jabber-alert-message-hooks '(jabber-message-echo
jabber-message-scroll)
"Hooks run when a new message arrives.
Arguments are FROM, BUFFER, TEXT and TITLE. FROM is the JID of
the sender, BUFFER is the the buffer where the message can be
read, and TEXT is the text of the message. TITLE is the string
returned by `jabber-alert-message-function' for these arguments,
so that hooks do not have to call it themselves.
This hook is meant for user customization of message alerts. For
other uses, see `jabber-message-hooks'."
:type 'hook
:options '(jabber-message-beep
jabber-message-wave
jabber-message-echo
jabber-message-switch
jabber-message-display
jabber-message-scroll)
:group 'jabber-alerts)
(defvar jabber-message-hooks nil
"Internal hooks run when a new message arrives.
This hook works just like `jabber-alert-message-hooks', except that
it's not meant to be customized by the user.")
(defcustom jabber-alert-message-function
'jabber-message-default-message
"Function for constructing short message alert messages.
Arguments are FROM, BUFFER, and TEXT. This function should return a
string containing an appropriate text message, or nil if no message
should be displayed.
The provided hooks displaying a text message get it from this function,
and show no message if it returns nil. Other hooks do what they do
every time."
:type 'function
:group 'jabber-alerts)
(defcustom jabber-alert-muc-hooks '(jabber-muc-echo jabber-muc-scroll)
"Hooks run when a new MUC message arrives.
Arguments are NICK, GROUP, BUFFER, TEXT and TITLE. NICK is the
nickname of the sender. GROUP is the JID of the group. BUFFER
is the the buffer where the message can be read, and TEXT is the
text of the message. TITLE is the string returned by
`jabber-alert-muc-function' for these arguments, so that hooks do
not have to call it themselves."
:type 'hook
:options '(jabber-muc-beep
jabber-muc-wave
jabber-muc-echo
jabber-muc-switch
jabber-muc-display
jabber-muc-scroll)
:group 'jabber-alerts)
(defvar jabber-muc-hooks '()
"Internal hooks run when a new MUC message arrives.
This hook works just like `jabber-alert-muc-hooks', except that
it's not meant to be customized by the user.")
(defcustom jabber-alert-muc-function
'jabber-muc-default-message
"Function for constructing short message alert messages.
Arguments are NICK, GROUP, BUFFER, and TEXT. This function
should return a string containing an appropriate text message, or
nil if no message should be displayed.
The provided hooks displaying a text message get it from this function,
and show no message if it returns nil. Other hooks do what they do
every time."
:type 'function
:group 'jabber-alerts)
(defcustom jabber-alert-presence-hooks
'(jabber-presence-echo)
"Hooks run when a user's presence changes.
Arguments are WHO, OLDSTATUS, NEWSTATUS, STATUSTEXT and
PROPOSED-ALERT. WHO is a symbol whose text is the JID of the contact,
and which has various interesting properties. OLDSTATUS is the old
presence or nil if disconnected. NEWSTATUS is the new presence, or
one of \"subscribe\", \"unsubscribe\", \"subscribed\" and
\"unsubscribed\". TITLE is the string returned by
`jabber-alert-presence-message-function' for these arguments."
:type 'hook
:options '(jabber-presence-beep
jabber-presence-wave
jabber-presence-switch
jabber-presence-display
jabber-presence-echo)
:group 'jabber-alerts)
(defvar jabber-presence-hooks '(jabber-presence-watch)
"Internal hooks run when a user's presence changes.
This hook works just like `jabber-alert-presence-hooks', except that
it's not meant to be customized by the user.")
(defcustom jabber-alert-presence-message-function
'jabber-presence-default-message
"Function for constructing title of presence alert messages.
Arguments are WHO, OLDSTATUS, NEWSTATUS and STATUSTEXT. See
`jabber-alert-presence-hooks' for documentation. This function
should return a string containing an appropriate text message, or nil
if no message should be displayed.
The provided hooks displaying a text message get it from this function.
All hooks refrain from action if this function returns nil."
:type 'function
:group 'jabber-alerts)
(defcustom jabber-alert-info-message-hooks '(jabber-info-display jabber-info-echo)
"Hooks run when an info request is completed.
First argument is WHAT, a symbol telling the kind of info request completed.
That might be 'roster, for requested roster updates, and 'browse, for
browse requests. Second argument in BUFFER, a buffer containing the result.
Third argument is PROPOSED-ALERT, containing the string returned by
`jabber-alert-info-message-function' for these arguments."
:type 'hook
:options '(jabber-info-beep
jabber-info-wave
jabber-info-echo
jabber-info-switch
jabber-info-display)
:group 'jabber-alerts)
(defvar jabber-info-message-hooks '()
"Internal hooks run when an info request is completed.
This hook works just like `jabber-alert-info-message-hooks',
except that it's not meant to be customized by the user.")
(defcustom jabber-alert-info-message-function
'jabber-info-default-message
"Function for constructing info alert messages.
Arguments are WHAT, a symbol telling the kind of info request completed,
and BUFFER, a buffer containing the result."
:type 'function
:group 'jabber-alerts)
(defcustom jabber-info-message-alist
'((roster . "Roster display updated")
(browse . "Browse request completed"))
"Alist for info alert messages, used by `jabber-info-default-message'."
:type '(alist :key-type symbol :value-type string
:options (roster browse))
:group 'jabber-alerts)
(defcustom jabber-alert-message-wave ""
"A sound file to play when a message arrived.
See `jabber-alert-message-wave-alist' if you want other sounds
for specific contacts."
:type 'file
:group 'jabber-alerts)
(defcustom jabber-alert-message-wave-alist nil
"Specific sound files for messages from specific contacts.
The keys are regexps matching the JID, and the values are sound
files."
:type '(alist :key-type regexp :value-type file)
:group 'jabber-alerts)
(defcustom jabber-alert-muc-wave ""
"a sound file to play when a MUC message arrived"
:type 'file
:group 'jabber-alerts)
(defcustom jabber-alert-presence-wave ""
"a sound file to play when a presence arrived"
:type 'file
:group 'jabber-alerts)
(defcustom jabber-alert-presence-wave-alist nil
"Specific sound files for presence from specific contacts.
The keys are regexps matching the JID, and the values are sound
files."
:type '(alist :key-type regexp :value-type file)
:group 'jabber-alerts)
(defcustom jabber-alert-info-wave ""
"a sound file to play when an info query result arrived"
:type 'file
:group 'jabber-alerts)
(defcustom jabber-play-sound-file 'play-sound-file
"a function to call to play alert sound files"
:type 'function
:group 'jabber-alerts)
(defmacro define-jabber-alert (name docstring function)
"Define a new family of external alert hooks.
Use this macro when your hooks do nothing except displaying a string
in some new innovative way. You write a string display function, and
this macro does all the boring and repetitive work.
NAME is the name of the alert family. The resulting hooks will be
called jabber-{message,muc,presence,info}-NAME.
DOCSTRING is the docstring to use for those hooks.
FUNCTION is a function that takes one argument, a string,
and displays it in some meaningful way. It can be either a
lambda form or a quoted function name.
The created functions are inserted as options in Customize.
Examples:
\(define-jabber-alert foo \"Send foo alert\" 'foo-message)
\(define-jabber-alert bar \"Send bar alert\"
(lambda (msg) (bar msg 42)))"
(let ((sn (symbol-name name)))
(let ((msg (intern (format "jabber-message-%s" sn)))
(muc (intern (format "jabber-muc-%s" sn)))
(pres (intern (format "jabber-presence-%s" sn)))
(info (intern (format "jabber-info-%s" sn))))
`(progn
(defun ,msg (from buffer text title)
,docstring
(when title
(funcall ,function text title)))
(pushnew (quote ,msg) (get 'jabber-alert-message-hooks 'custom-options))
(defun ,muc (nick group buffer text title)
,docstring
(when title
(funcall ,function text title)))
(pushnew (quote ,muc) (get 'jabber-alert-muc-hooks 'custom-options))
(defun ,pres (who oldstatus newstatus statustext title)
,docstring
(when title
(funcall ,function statustext title)))
(pushnew (quote ,pres) (get 'jabber-alert-presence-hooks 'custom-options))
(defun ,info (infotype buffer text)
,docstring
(when text
(funcall ,function text)))
(pushnew (quote ,info) (get 'jabber-alert-info-message-hooks 'custom-options))))))
;; Alert hooks
(define-jabber-alert echo "Show a message in the echo area"
(lambda (text &optional title) (message "%s" (or title text))))
(define-jabber-alert beep "Beep on event"
(lambda (&rest ignore) (beep)))
;; Message alert hooks
(defun jabber-message-default-message (from buffer text)
(when (or jabber-message-alert-same-buffer
(not (memq (selected-window) (get-buffer-window-list buffer))))
(if (jabber-muc-sender-p from)
(format "Private message from %s in %s"
(jabber-jid-resource from)
(jabber-jid-displayname (jabber-jid-user from)))
(format "Message from %s" (jabber-jid-displayname from)))))
(defcustom jabber-message-alert-same-buffer t
"If nil, don't display message alerts for the current buffer."
:type 'boolean
:group 'jabber-alerts)
(defcustom jabber-muc-alert-self nil
"If nil, don't display MUC alerts for your own messages."
:type 'boolean
:group 'jabber-alerts)
(defun jabber-message-wave (from buffer text title)
"Play the wave file specified in `jabber-alert-message-wave'"
(when title
(let* ((case-fold-search t)
(bare-jid (jabber-jid-user from))
(sound-file (or (dolist (entry jabber-alert-message-wave-alist)
(when (string-match (car entry) bare-jid)
(return (cdr entry))))
jabber-alert-message-wave)))
(unless (equal sound-file "")
(funcall jabber-play-sound-file sound-file)))))
(defun jabber-message-display (from buffer text title)
"Display the buffer where a new message has arrived."
(when title
(display-buffer buffer)))
(defun jabber-message-switch (from buffer text title)
"Switch to the buffer where a new message has arrived."
(when title
(switch-to-buffer buffer)))
(defun jabber-message-scroll (from buffer text title)
"Scroll all nonselected windows where the chat buffer is displayed."
;; jabber-chat-buffer-display will DTRT with point in the buffer.
;; But this change will not take effect in nonselected windows.
;; Therefore we do that manually here.
;;
;; There are three cases:
;; 1. The user started typing a message in this window. Point is
;; greater than jabber-point-insert. In that case, we don't
;; want to move point.
;; 2. Point was at the end of the buffer, but no message was being
;; typed. After displaying the message, point is now close to
;; the end of the buffer. We advance it to the end.
;; 3. The user was perusing history in this window. There is no
;; simple way to distinguish this from 2, so the user loses.
(let ((windows (get-buffer-window-list buffer nil t))
(new-point-max (with-current-buffer buffer (point-max))))
(dolist (w windows)
(unless (eq w (selected-window))
(set-window-point w new-point-max)))))
;; MUC alert hooks
(defun jabber-muc-default-message (nick group buffer text)
(when (or jabber-message-alert-same-buffer
(not (memq (selected-window) (get-buffer-window-list buffer))))
(if nick
(when (or jabber-muc-alert-self
(not (string= nick (cdr (assoc group *jabber-active-groupchats*)))))
(format "Message from %s in %s" nick (jabber-jid-displayname
group)))
(format "Message in %s" (jabber-jid-displayname group)))))
(defun jabber-muc-wave (nick group buffer text title)
"Play the wave file specified in `jabber-alert-muc-wave'"
(when title
(funcall jabber-play-sound-file jabber-alert-muc-wave)))
(defun jabber-muc-display (nick group buffer text title)
"Display the buffer where a new message has arrived."
(when title
(display-buffer buffer)))
(defun jabber-muc-switch (nick group buffer text title)
"Switch to the buffer where a new message has arrived."
(when title
(switch-to-buffer buffer)))
(defun jabber-muc-scroll (nick group buffer text title)
"Scroll buffer even if it is in an unselected window."
(jabber-message-scroll nil buffer nil nil))
;; Presence alert hooks
(defun jabber-presence-default-message (who oldstatus newstatus statustext)
"This function returns nil if OLDSTATUS and NEWSTATUS are equal, and in other
cases a string of the form \"'name' (jid) is now NEWSTATUS (STATUSTEXT)\".
This function is not called directly, but is the default for
`jabber-alert-presence-message-function'."
(cond
((equal oldstatus newstatus)
nil)
(t
(let ((formattedname
(if (> (length (get who 'name)) 0)
(get who 'name)
(symbol-name who)))
(formattedstatus
(or
(cdr (assoc newstatus
'(("subscribe" . " requests subscription to your presence")
("subscribed" . " has granted presence subscription to you")
("unsubscribe" . " no longer subscribes to your presence")
("unsubscribed" . " cancels your presence subscription"))))
(concat " is now "
(or
(cdr (assoc newstatus jabber-presence-strings))
newstatus)))))
(concat formattedname formattedstatus)))))
(defun jabber-presence-only-chat-open-message (who oldstatus newstatus statustext)
"This function returns the same as `jabber-presence-default-message' but only
if there is a chat buffer open for WHO, keeping the amount of presence messages
at a more manageable level when there are lots of users.
This function is not called directly, but can be used as the value for
`jabber-alert-presence-message-function'."
(when (get-buffer (jabber-chat-get-buffer (jabber-xml-get-attribute xml-data 'from)))
(jabber-presence-default-message who oldstatus newstatus statustext)))
(defun jabber-presence-wave (who oldstatus newstatus statustext proposed-alert)
"Play the wave file specified in `jabber-alert-presence-wave'"
(when proposed-alert
(let* ((case-fold-search t)
(bare-jid (symbol-name who))
(sound-file (or (dolist (entry jabber-alert-presence-wave-alist)
(when (string-match (car entry) bare-jid)
(return (cdr entry))))
jabber-alert-presence-wave)))
(unless (equal sound-file "")
(funcall jabber-play-sound-file sound-file)))))
;; This is now defined in jabber-roster.el.
;; (defun jabber-presence-update-roster (who oldstatus newstatus statustext proposed-alert)
;; "Update the roster display by calling `jabber-display-roster'"
;; (jabber-display-roster))
(defun jabber-presence-display (who oldstatus newstatus statustext proposed-alert)
"Display the roster buffer"
(when proposed-alert
(display-buffer jabber-roster-buffer)))
(defun jabber-presence-switch (who oldstatus newstatus statustext proposed-alert)
"Switch to the roster buffer"
(when proposed-alert
(switch-to-buffer jabber-roster-buffer)))
;;; Info alert hooks
(defun jabber-info-default-message (infotype buffer)
"Function for constructing info alert messages.
The argument is INFOTYPE, a symbol telling the kind of info request completed.
This function uses `jabber-info-message-alist' to find a message."
(concat (cdr (assq infotype jabber-info-message-alist))
" (buffer "(buffer-name buffer) ")"))
(defun jabber-info-wave (infotype buffer proposed-alert)
"Play the wave file specified in `jabber-alert-info-wave'"
(if proposed-alert
(funcall jabber-play-sound-file jabber-alert-info-wave)))
(defun jabber-info-display (infotype buffer proposed-alert)
"Display buffer of completed request"
(when proposed-alert
(display-buffer buffer)))
(defun jabber-info-switch (infotype buffer proposed-alert)
"Switch to buffer of completed request"
(when proposed-alert
(switch-to-buffer buffer)))
;;; Personal alert hooks
(defmacro define-personal-jabber-alert (name)
"From ALERT function, make ALERT-personal function. Makes sence only for MUC."
(let ((sn (symbol-name name)))
(let ((func (intern (format "%s-personal" sn))))
`(progn
(defun ,func (nick group buffer text title)
(if (jabber-muc-looks-like-personal-p text group)
(,name nick group buffer text title)))
(pushnew (quote ,func) (get 'jabber-alert-muc-hooks 'custom-options)))))
)
(define-personal-jabber-alert jabber-muc-beep)
(define-personal-jabber-alert jabber-muc-wave)
(define-personal-jabber-alert jabber-muc-echo)
(define-personal-jabber-alert jabber-muc-switch)
(define-personal-jabber-alert jabber-muc-display)
(defcustom jabber-autoanswer-alist nil
"Specific phrases to autoanswer on specific message.
The keys are regexps matching the incoming message text, and the values are
autoanswer phrase."
:type '(alist :key-type regexp :value-type string)
:group 'jabber-alerts)
(defun jabber-autoanswer-answer (from buffer text proposed-alert)
"Answer automaticaly when incoming text matches first element
of `jabber-autoanswer-alist'"
(when (and from buffer text proposed-alert jabber-autoanswer-alist)
(let ((message
(dolist (entry jabber-autoanswer-alist)
(when (string-match (car entry) text)
(return (cdr entry))))))
(if message
(jabber-chat-send jabber-buffer-connection message)))
))
(pushnew 'jabber-autoanswer-answer (get 'jabber-alert-message-hooks 'custom-options))
(defun jabber-autoanswer-answer-muc (nick group buffer text proposed-alert)
"Answer automaticaly when incoming text matches first element
of `jabber-autoanswer-alist'"
(when (and nick group buffer text proposed-alert jabber-autoanswer-alist)
(let ((message
(dolist (entry jabber-autoanswer-alist)
(when (string-match (car entry) text)
(return (cdr entry))))))
(if message
(jabber-chat-send jabber-buffer-connection message)))
))
(pushnew 'jabber-autoanswer-answer-muc (get 'jabber-alert-muc-hooks 'custom-options))
(provide 'jabber-alert)
;;; arch-tag: 725bd73e-c613-4fdc-a11d-3392a7598d4f

View File

@ -1,211 +0,0 @@
;;; jabber-autoaway.el --- change status to away after idleness
;; Copyright (C) 2010 - Kirill A. Korinskiy - catap@catap.ru
;; Copyright (C) 2010 - Terechkov Evgenii - evg@altlinux.org
;; Copyright (C) 2006, 2008 Magnus Henoch
;; Author: Magnus Henoch <mange@freemail.hu>
;; This file 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.
;; This file 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 GNU Emacs; see the file COPYING. If not, write to
;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
;; Boston, MA 02110-1301, USA.
(eval-when-compile (require 'cl))
(require 'time-date)
(defgroup jabber-autoaway nil
"Change status to away after idleness"
:group 'jabber)
(defcustom jabber-autoaway-methods
(if (fboundp 'jabber-autoaway-method)
(list jabber-autoaway-method)
(list 'jabber-current-idle-time
'jabber-xprintidle-get-idle-time
'jabber-termatime-get-idle-time))
"Methods used to keep track of idleness.
This is a list of functions that takes no arguments, and returns the
number of seconds since the user was active, or nil on error."
:group 'jabber-autoaway
:options '(jabber-current-idle-time
jabber-xprintidle-get-idle-time
jabber-termatime-get-idle-time))
(defcustom jabber-autoaway-timeout 5
"Minutes of inactivity before changing status to away"
:group 'jabber-autoaway
:type 'number)
(defcustom jabber-autoaway-xa-timeout 10
"Minutes of inactivity before changing status to xa. Set to 0 to disable."
:group 'jabber-autoaway
:type 'number)
(defcustom jabber-autoaway-status "Idle"
"Status string for autoaway"
:group 'jabber-autoaway
:type 'string)
(defcustom jabber-autoaway-xa-status "Extended away"
"Status string for autoaway in xa state"
:group 'jabber-autoaway
:type 'string)
(defcustom jabber-autoaway-priority nil
"Priority for autoaway.
If nil, don't change priority. See the manual for more
information about priority."
:group 'jabber-autoaway
:type '(choice (const :tag "Don't change")
(integer :tag "Priority"))
:link '(info-link "(jabber)Presence"))
(defcustom jabber-autoaway-xa-priority nil
"Priority for autoaway in xa state.
If nil, don't change priority. See the manual for more
information about priority."
:group 'jabber-autoaway
:type '(choice (const :tag "Don't change")
(integer :tag "Priority"))
:link '(info-link "(jabber)Presence"))
(defcustom jabber-xprintidle-program (executable-find "xprintidle")
"Name of the xprintidle program"
:group 'jabber-autoaway
:type 'string)
(defcustom jabber-autoaway-verbose nil
"If nil, don't print autoaway status messages."
:group 'jabber-autoaway
:type 'boolean)
(defvar jabber-autoaway-timer nil)
(defvar jabber-autoaway-last-idle-time nil
"Seconds of idle time the last time we checked.
This is used to detect whether the user has become unidle.")
(defun jabber-autoaway-message (&rest args)
(when jabber-autoaway-verbose
(apply #'message args)))
;;;###autoload
(defun jabber-autoaway-start (&optional ignored)
"Start autoaway timer.
The IGNORED argument is there so you can put this function in
`jabber-post-connect-hooks'."
(interactive)
(unless jabber-autoaway-timer
(setq jabber-autoaway-timer
(run-with-timer (* jabber-autoaway-timeout 60) nil #'jabber-autoaway-timer))
(jabber-autoaway-message "Autoaway timer started")))
(defun jabber-autoaway-stop ()
"Stop autoaway timer."
(interactive)
(when jabber-autoaway-timer
(jabber-cancel-timer jabber-autoaway-timer)
(setq jabber-autoaway-timer nil)
(jabber-autoaway-message "Autoaway timer stopped")))
(defun jabber-autoaway-get-idle-time ()
"Get idle time in seconds according to jabber-autoaway-methods.
Return nil on error."
(car (sort (mapcar 'funcall jabber-autoaway-methods) (lambda (a b) (if a (if b (< a b) t) nil)))))
(defun jabber-autoaway-timer ()
;; We use one-time timers, so reset the variable.
(setq jabber-autoaway-timer nil)
(let ((idle-time (jabber-autoaway-get-idle-time)))
(when (numberp idle-time)
;; Has "idle timeout" passed?
(if (> idle-time (* 60 jabber-autoaway-timeout))
;; If so, mark ourselves idle.
(jabber-autoaway-set-idle)
;; Else, start a timer for the remaining amount.
(setq jabber-autoaway-timer
(run-with-timer (- (* 60 jabber-autoaway-timeout) idle-time)
nil #'jabber-autoaway-timer))))))
(defun jabber-autoaway-set-idle (&optional xa)
(jabber-autoaway-message "Autoaway triggered")
;; Send presence, unless the user has set a custom presence
(unless (member *jabber-current-show* '("xa" "dnd"))
(jabber-send-presence
(if xa "xa" "away")
(if (or (string= *jabber-current-status* jabber-default-status) (string= *jabber-current-status* jabber-autoaway-status)) (if xa jabber-autoaway-xa-status jabber-autoaway-status) *jabber-current-status*)
(or (if xa jabber-autoaway-priority jabber-autoaway-xa-priority) *jabber-current-priority*)))
(setq jabber-autoaway-last-idle-time (jabber-autoaway-get-idle-time))
;; Run unidle timer every 10 seconds (if xa specified, timer already running)
(unless xa
(setq jabber-autoaway-timer (run-with-timer 10 10
#'jabber-autoaway-maybe-unidle))))
(defun jabber-autoaway-maybe-unidle ()
(let ((idle-time (jabber-autoaway-get-idle-time)))
(jabber-autoaway-message "Idle for %d seconds" idle-time)
(if (member *jabber-current-show* '("xa" "away"))
;; As long as idle time increases monotonically, stay idle.
(if (> idle-time jabber-autoaway-last-idle-time)
(progn
;; Has "Xa timeout" passed?
(if (and (> jabber-autoaway-xa-timeout 0) (> idle-time (* 60 jabber-autoaway-xa-timeout)))
;; iIf so, mark ourselves xa.
(jabber-autoaway-set-idle t))
(setq jabber-autoaway-last-idle-time idle-time))
;; But if it doesn't, go back to unidle state.
(jabber-autoaway-message "Back to unidle")
;; But don't mess with the user's custom presence.
(if (or (string= *jabber-current-status* jabber-autoaway-status) (string= *jabber-current-status* jabber-autoaway-xa-status))
(jabber-send-default-presence)
(progn
(jabber-send-presence jabber-default-show *jabber-current-status* jabber-default-priority)
(jabber-autoaway-message "%S /= %S - not resetting presence" *jabber-current-status* jabber-autoaway-status)))
(jabber-autoaway-stop)
(jabber-autoaway-start)))))
(defun jabber-xprintidle-get-idle-time ()
"Get idle time through the xprintidle program."
(when jabber-xprintidle-program
(with-temp-buffer
(when (zerop (call-process jabber-xprintidle-program
nil t))
(/ (string-to-number (buffer-string)) 1000.0)))))
(defun jabber-termatime-get-idle-time ()
"Get idle time through atime of terminal.
The method for finding the terminal only works on GNU/Linux."
(let ((terminal (cond
((file-exists-p "/proc/self/fd/0")
"/proc/self/fd/0")
(t
nil))))
(when terminal
(let* ((atime-of-tty (nth 4 (file-attributes terminal)))
(diff (time-to-seconds (time-since atime-of-tty))))
(when (> diff 0)
diff)))))
(defun jabber-current-idle-time ()
"Get idle time through `current-idle-time'.
`current-idle-time' was introduced in Emacs 22."
(if (fboundp 'current-idle-time)
(let ((idle-time (current-idle-time)))
(if (null idle-time)
0
(float-time idle-time)))))
(provide 'jabber-autoaway)
;; arch-tag: 5bcea14c-842d-11da-a120-000a95c2fcd0

View File

@ -1,852 +0,0 @@
;;; jabber-autoloads.el --- automatically extracted autoloads -*- lexical-binding: t -*-
;;
;;; Code:
(add-to-list 'load-path (directory-file-name
(or (file-name-directory #$) (car load-path))))
;;;### (autoloads nil "jabber" "jabber.el" (0 0 0 0))
;;; Generated autoloads from jabber.el
(defvar jabber-account-list nil "\
List of Jabber accounts.
Each element of the list is a cons cell describing a Jabber account,
where the car is a JID and the CDR is an alist.
JID is a full Jabber ID string (e.g. foo@bar.tld). You can also
specify the resource (e.g. foo@bar.tld/emacs).
The following keys can be present in the alist:
:password is a string to authenticate ourself against the server.
It can be empty. If you don't want to store your password in your
Emacs configuration, try auth-source (info node `(auth)Top').
:network-server is a string identifying the address to connect to,
if it's different from the server part of the JID.
:port is the port to use (default depends on connection type).
:connection-type is a symbol. Valid symbols are `starttls',
`network' and `ssl'.
Only JID is mandatory. The rest can be guessed at run-time.
Examples:
Two accounts without any special configuration:
\((\"foo@example.com\") (\"bar@example.net\"))
One disabled account with a non-standard port:
\((\"romeo@montague.net\" (:port . 5242) (:disabled . t)))
If you don't have SRV and STARTTLS capabilities in your Emacs,
configure a Google Talk account like this:
\((\"username@gmail.com\"
(:network-server . \"talk.google.com\")
(:connection-type . ssl)))")
(custom-autoload 'jabber-account-list "jabber" t)
(defvar *jabber-current-status* nil "\
the users current presence status")
(defvar *jabber-current-show* nil "\
the users current presence show")
(defvar *jabber-current-priority* nil "\
the user's current priority")
(defconst jabber-presence-faces '(("" . jabber-roster-user-online) ("away" . jabber-roster-user-away) ("xa" . jabber-roster-user-xa) ("dnd" . jabber-roster-user-dnd) ("chat" . jabber-roster-user-chatty) ("error" . jabber-roster-user-error) (nil . jabber-roster-user-offline)) "\
Mapping from presence types to faces")
(autoload 'jabber-customize "jabber" "\
customize jabber options" t nil)
(autoload 'jabber-info "jabber" "\
open jabber.el manual" t nil)
(register-definition-prefixes "jabber" '("*jabber-status-history*" "jabber-"))
;;;***
;;;### (autoloads nil "jabber-activity" "jabber-activity.el" (0 0
;;;;;; 0 0))
;;; Generated autoloads from jabber-activity.el
(defvar jabber-activity-mode t "\
Non-nil if Jabber-Activity mode is enabled.
See the `jabber-activity-mode' command
for a description of this minor mode.
Setting this variable directly does not take effect;
either customize it (see the info node `Easy Customization')
or call the function `jabber-activity-mode'.")
(custom-autoload 'jabber-activity-mode "jabber-activity" nil)
(autoload 'jabber-activity-mode "jabber-activity" "\
Toggle display of activity in hidden jabber buffers in the mode line.
With a numeric arg, enable this display if arg is positive.
\(fn &optional ARG)" t nil)
(register-definition-prefixes "jabber-activity" '("jabber-activity-"))
;;;***
;;;### (autoloads nil "jabber-ahc" "jabber-ahc.el" (0 0 0 0))
;;; Generated autoloads from jabber-ahc.el
(register-definition-prefixes "jabber-ahc" '("jabber-ahc-"))
;;;***
;;;### (autoloads nil "jabber-ahc-presence" "jabber-ahc-presence.el"
;;;;;; (0 0 0 0))
;;; Generated autoloads from jabber-ahc-presence.el
(register-definition-prefixes "jabber-ahc-presence" '("jabber-ahc-presence"))
;;;***
;;;### (autoloads nil "jabber-alert" "jabber-alert.el" (0 0 0 0))
;;; Generated autoloads from jabber-alert.el
(register-definition-prefixes "jabber-alert" '("beep" "define-" "echo" "jabber-"))
;;;***
;;;### (autoloads nil "jabber-autoaway" "jabber-autoaway.el" (0 0
;;;;;; 0 0))
;;; Generated autoloads from jabber-autoaway.el
(autoload 'jabber-autoaway-start "jabber-autoaway" "\
Start autoaway timer.
The IGNORED argument is there so you can put this function in
`jabber-post-connect-hooks'.
\(fn &optional IGNORED)" t nil)
(register-definition-prefixes "jabber-autoaway" '("jabber-"))
;;;***
;;;### (autoloads nil "jabber-avatar" "jabber-avatar.el" (0 0 0 0))
;;; Generated autoloads from jabber-avatar.el
(register-definition-prefixes "jabber-avatar" '("avatar" "jabber-"))
;;;***
;;;### (autoloads nil "jabber-awesome" "jabber-awesome.el" (0 0 0
;;;;;; 0))
;;; Generated autoloads from jabber-awesome.el
(register-definition-prefixes "jabber-awesome" '("awesome" "jabber-awesome-"))
;;;***
;;;### (autoloads nil "jabber-bookmarks" "jabber-bookmarks.el" (0
;;;;;; 0 0 0))
;;; Generated autoloads from jabber-bookmarks.el
(autoload 'jabber-get-conference-data "jabber-bookmarks" "\
Get bookmark data for CONFERENCE-JID.
KEY may be nil or one of :name, :autojoin, :nick and :password.
If KEY is nil, a plist containing the above keys is returned.
CONT is called when the result is available, with JC and the
result as arguments. If CONT is nil, return the requested data
immediately, and return nil if it is not in the cache.
\(fn JC CONFERENCE-JID CONT &optional KEY)" nil nil)
(autoload 'jabber-parse-conference-bookmark "jabber-bookmarks" "\
Convert a <conference/> tag into a plist.
The plist may contain the keys :jid, :name, :autojoin,
:nick and :password.
\(fn NODE)" nil nil)
(autoload 'jabber-get-bookmarks "jabber-bookmarks" "\
Retrieve bookmarks (if needed) and call CONT.
Arguments to CONT are JC and the bookmark list. CONT will be
called as the result of a filter function or a timer.
If REFRESH is non-nil, always fetch bookmarks.
\(fn JC CONT &optional REFRESH)" nil nil)
(autoload 'jabber-get-bookmarks-from-cache "jabber-bookmarks" "\
Return cached bookmarks for JC.
If bookmarks have not yet been fetched by `jabber-get-bookmarks',
return nil.
\(fn JC)" nil nil)
(autoload 'jabber-edit-bookmarks "jabber-bookmarks" "\
Create a buffer for editing bookmarks interactively.
\(fn JC)" t nil)
(register-definition-prefixes "jabber-bookmarks" '("jabber-"))
;;;***
;;;### (autoloads nil "jabber-browse" "jabber-browse.el" (0 0 0 0))
;;; Generated autoloads from jabber-browse.el
(register-definition-prefixes "jabber-browse" '("jabber-"))
;;;***
;;;### (autoloads nil "jabber-chat" "jabber-chat.el" (0 0 0 0))
;;; Generated autoloads from jabber-chat.el
(defvar jabber-chatting-with nil "\
JID of the person you are chatting with")
(autoload 'jabber-chat-get-buffer "jabber-chat" "\
Return the chat buffer for chatting with CHAT-WITH (bare or full JID).
Either a string or a buffer is returned, so use `get-buffer' or
`get-buffer-create'.
\(fn CHAT-WITH)" nil nil)
(register-definition-prefixes "jabber-chat" '("jabber-"))
;;;***
;;;### (autoloads nil "jabber-chatbuffer" "jabber-chatbuffer.el"
;;;;;; (0 0 0 0))
;;; Generated autoloads from jabber-chatbuffer.el
(defvar jabber-buffer-connection nil "\
The connection used by this buffer.")
(make-variable-buffer-local 'jabber-buffer-connection)
(register-definition-prefixes "jabber-chatbuffer" '("jabber-"))
;;;***
;;;### (autoloads nil "jabber-chatstates" "jabber-chatstates.el"
;;;;;; (0 0 0 0))
;;; Generated autoloads from jabber-chatstates.el
(register-definition-prefixes "jabber-chatstates" '("jabber-"))
;;;***
;;;### (autoloads nil "jabber-compose" "jabber-compose.el" (0 0 0
;;;;;; 0))
;;; Generated autoloads from jabber-compose.el
(autoload 'jabber-compose "jabber-compose" "\
Create a buffer for composing a Jabber message.
\(fn JC &optional RECIPIENT)" t nil)
(register-definition-prefixes "jabber-compose" '("jabber-compose-send"))
;;;***
;;;### (autoloads nil "jabber-conn" "jabber-conn.el" (0 0 0 0))
;;; Generated autoloads from jabber-conn.el
(register-definition-prefixes "jabber-conn" '("*jabber-virtual-server-function*" "jabber-"))
;;;***
;;;### (autoloads nil "jabber-console" "jabber-console.el" (0 0 0
;;;;;; 0))
;;; Generated autoloads from jabber-console.el
(autoload 'jabber-process-console "jabber-console" "\
Log XML-DATA i/o as XML in \"*-jabber-console-JID-*\" buffer
\(fn JC DIRECTION XML-DATA)" nil nil)
(register-definition-prefixes "jabber-console" '("jabber-"))
;;;***
;;;### (autoloads nil "jabber-core" "jabber-core.el" (0 0 0 0))
;;; Generated autoloads from jabber-core.el
(autoload 'jabber-connect-all "jabber" "Connect to all configured Jabber accounts.\nSee `jabber-account-list'.\nIf no accounts are configured (or ARG supplied), call `jabber-connect' interactively." t)
(autoload 'jabber-connect "jabber" "Connect to the Jabber server and start a Jabber XML stream.\nWith prefix argument, register a new account.\nWith double prefix argument, specify more connection details." t)
(register-definition-prefixes "jabber-core" '("*jabber-" "jabber-"))
;;;***
;;;### (autoloads nil "jabber-disco" "jabber-disco.el" (0 0 0 0))
;;; Generated autoloads from jabber-disco.el
(eval-after-load "jabber-core" '(add-to-list 'jabber-presence-chain #'jabber-process-caps))
(autoload 'jabber-process-caps "jabber-disco" "\
Look for entity capabilities in presence stanzas.
\(fn JC XML-DATA)" nil nil)
(autoload 'jabber-disco-advertise-feature "jabber-disco" "\
\(fn FEATURE)" nil nil)
(autoload 'jabber-caps-presence-element "jabber-disco" "\
\(fn JC)" nil nil)
(eval-after-load "jabber-presence" '(add-to-list 'jabber-presence-element-functions #'jabber-caps-presence-element))
(register-definition-prefixes "jabber-disco" '("jabber-"))
;;;***
;;;### (autoloads nil "jabber-events" "jabber-events.el" (0 0 0 0))
;;; Generated autoloads from jabber-events.el
(register-definition-prefixes "jabber-events" '("jabber-"))
;;;***
;;;### (autoloads nil "jabber-export" "jabber-export.el" (0 0 0 0))
;;; Generated autoloads from jabber-export.el
(autoload 'jabber-export-roster "jabber-export" "\
Export roster for connection JC.
\(fn JC)" t nil)
(autoload 'jabber-import-roster "jabber-export" "\
Create buffer for roster import for connection JC from FILE.
\(fn JC FILE)" t nil)
(register-definition-prefixes "jabber-export" '("jabber-"))
;;;***
;;;### (autoloads nil "jabber-feature-neg" "jabber-feature-neg.el"
;;;;;; (0 0 0 0))
;;; Generated autoloads from jabber-feature-neg.el
(register-definition-prefixes "jabber-feature-neg" '("jabber-fn-"))
;;;***
;;;### (autoloads nil "jabber-ft-client" "jabber-ft-client.el" (0
;;;;;; 0 0 0))
;;; Generated autoloads from jabber-ft-client.el
(register-definition-prefixes "jabber-ft-client" '("jabber-ft-"))
;;;***
;;;### (autoloads nil "jabber-ft-common" "jabber-ft-common.el" (0
;;;;;; 0 0 0))
;;; Generated autoloads from jabber-ft-common.el
(register-definition-prefixes "jabber-ft-common" '("jabber-ft-"))
;;;***
;;;### (autoloads nil "jabber-ft-server" "jabber-ft-server.el" (0
;;;;;; 0 0 0))
;;; Generated autoloads from jabber-ft-server.el
(register-definition-prefixes "jabber-ft-server" '("jabber-ft-"))
;;;***
;;;### (autoloads nil "jabber-gmail" "jabber-gmail.el" (0 0 0 0))
;;; Generated autoloads from jabber-gmail.el
(autoload 'jabber-gmail-subscribe "jabber-gmail" "\
Subscribe to gmail notifications.
See http://code.google.com/apis/talk/jep_extensions/usersettings.html#4
\(fn JC)" t nil)
(autoload 'jabber-gmail-query "jabber-gmail" "\
Request mail information from the Google Talk server (a.k.a. one shot query).
See http://code.google.com/apis/talk/jep_extensions/gmail.html#requestmail
\(fn JC)" t nil)
(register-definition-prefixes "jabber-gmail" '("jabber-gmail-"))
;;;***
;;;### (autoloads nil "jabber-history" "jabber-history.el" (0 0 0
;;;;;; 0))
;;; Generated autoloads from jabber-history.el
(register-definition-prefixes "jabber-history" '("jabber-"))
;;;***
;;;### (autoloads nil "jabber-iq" "jabber-iq.el" (0 0 0 0))
;;; Generated autoloads from jabber-iq.el
(register-definition-prefixes "jabber-iq" '("*jabber-open-info-queries*" "jabber-"))
;;;***
;;;### (autoloads nil "jabber-keepalive" "jabber-keepalive.el" (0
;;;;;; 0 0 0))
;;; Generated autoloads from jabber-keepalive.el
(let ((loads (get 'jabber-keepalive 'custom-loads))) (if (member '"jabber-keepalive" loads) nil (put 'jabber-keepalive 'custom-loads (cons '"jabber-keepalive" loads))))
(autoload 'jabber-keepalive-start "jabber-keepalive" "\
Activate keepalive.
That is, regularly send a ping request to the server, and
disconnect if it doesn't answer. See `jabber-keepalive-interval'
and `jabber-keepalive-timeout'.
The JC argument makes it possible to add this function to
`jabber-post-connect-hooks'; it is ignored. Keepalive is activated
for all accounts regardless of the argument.
\(fn &optional JC)" t nil)
(autoload 'jabber-whitespace-ping-start "jabber-keepalive" "\
Start sending whitespace pings at regular intervals.
See `jabber-whitespace-ping-interval'.
The JC argument is ignored; whitespace pings are enabled for all
accounts.
\(fn &optional JC)" t nil)
(register-definition-prefixes "jabber-keepalive" '("jabber-"))
;;;***
;;;### (autoloads nil "jabber-keymap" "jabber-keymap.el" (0 0 0 0))
;;; Generated autoloads from jabber-keymap.el
(defvar jabber-global-keymap (let ((map (make-sparse-keymap))) (define-key map "\3" 'jabber-connect-all) (define-key map "\4" 'jabber-disconnect) (define-key map "\22" 'jabber-switch-to-roster-buffer) (define-key map "\n" 'jabber-chat-with) (define-key map "\f" 'jabber-activity-switch-to) (define-key map "\1" 'jabber-send-away-presence) (define-key map "\17" 'jabber-send-default-presence) (define-key map "\30" 'jabber-send-xa-presence) (define-key map "\20" 'jabber-send-presence) map) "\
Global Jabber keymap (usually under C-x C-j)")
(define-key ctl-x-map "\n" jabber-global-keymap)
(register-definition-prefixes "jabber-keymap" '("jabber-common-keymap"))
;;;***
;;;### (autoloads nil "jabber-libnotify" "jabber-libnotify.el" (0
;;;;;; 0 0 0))
;;; Generated autoloads from jabber-libnotify.el
(register-definition-prefixes "jabber-libnotify" '("jabber-libnotify-" "libnotify"))
;;;***
;;;### (autoloads nil "jabber-logon" "jabber-logon.el" (0 0 0 0))
;;; Generated autoloads from jabber-logon.el
(register-definition-prefixes "jabber-logon" '("jabber-"))
;;;***
;;;### (autoloads nil "jabber-menu" "jabber-menu.el" (0 0 0 0))
;;; Generated autoloads from jabber-menu.el
(defvar jabber-menu (let ((map (make-sparse-keymap "jabber-menu"))) (define-key-after map [jabber-menu-connect] '("Connect" . jabber-connect-all)) (define-key-after map [jabber-menu-disconnect] '(menu-item "Disconnect" jabber-disconnect :enable (bound-and-true-p jabber-connections))) (define-key-after map [jabber-menu-status] `(menu-item "Set Status" ,(make-sparse-keymap "set-status") :enable (bound-and-true-p jabber-connections))) (define-key map [jabber-menu-status jabber-menu-status-chat] '(menu-item "Chatty" (lambda nil (interactive) (jabber-send-presence "chat" (jabber-read-with-input-method "status message: " *jabber-current-status* '*jabber-status-history*) *jabber-current-priority*)) :button (:radio and (boundp '*jabber-current-show*) (equal *jabber-current-show* "chat")))) (define-key map [jabber-menu-status jabber-menu-status-dnd] '(menu-item "Do not Disturb" (lambda nil (interactive) (jabber-send-presence "dnd" (jabber-read-with-input-method "status message: " *jabber-current-status* '*jabber-status-history*) *jabber-current-priority*)) :button (:radio and (boundp '*jabber-current-show*) (equal *jabber-current-show* "dnd")))) (define-key map [jabber-menu-status jabber-menu-status-xa] '(menu-item "Extended Away" jabber-send-xa-presence :button (:radio and (boundp '*jabber-current-show*) (equal *jabber-current-show* "xa")))) (define-key map [jabber-menu-status jabber-menu-status-away] '(menu-item "Away" jabber-send-away-presence :button (:radio and (boundp '*jabber-current-show*) (equal *jabber-current-show* "away")))) (define-key map [jabber-menu-status jabber-menu-status-online] '(menu-item "Online" jabber-send-default-presence :button (:radio and (boundp '*jabber-current-show*) (equal *jabber-current-show* "")))) (define-key-after map [separator] '(menu-item "--")) (define-key-after map [jabber-menu-chat-with] '(menu-item "Chat with..." jabber-chat-with :enable (bound-and-true-p jabber-connections))) (define-key-after map [jabber-menu-nextmsg] '(menu-item "Next unread message" jabber-activity-switch-to :enable (bound-and-true-p jabber-activity-jids))) (define-key-after map [jabber-menu-send-subscription-request] '(menu-item "Send subscription request" jabber-send-subscription-request :enable (bound-and-true-p jabber-connections))) (define-key-after map [jabber-menu-roster] '("Switch to roster" . jabber-switch-to-roster-buffer)) (define-key-after map [separator2] '(menu-item "--")) (define-key-after map [jabber-menu-customize] '("Customize" . jabber-customize)) (define-key-after map [jabber-menu-info] '("Help" . jabber-info)) map))
(defvar jabber-display-menu 'maybe "\
Decide whether the \"Jabber\" menu is displayed in the menu bar.
If t, always display.
If nil, never display.
If maybe, display if jabber.el is installed under `package-user-dir', or
if any of `jabber-account-list' or `jabber-connections' is non-nil.")
(custom-autoload 'jabber-display-menu "jabber-menu" t)
(define-key-after (lookup-key global-map [menu-bar]) [jabber-menu] (list 'menu-item "Jabber" jabber-menu :visible '(or (eq jabber-display-menu t) (and (eq jabber-display-menu 'maybe) (or (bound-and-true-p jabber-account-list) (bound-and-true-p jabber-connections))))))
(register-definition-prefixes "jabber-menu" '("jabber-"))
;;;***
;;;### (autoloads nil "jabber-modeline" "jabber-modeline.el" (0 0
;;;;;; 0 0))
;;; Generated autoloads from jabber-modeline.el
(register-definition-prefixes "jabber-modeline" '("jabber-mode-line-"))
;;;***
;;;### (autoloads nil "jabber-muc" "jabber-muc.el" (0 0 0 0))
;;; Generated autoloads from jabber-muc.el
(defvar *jabber-active-groupchats* nil "\
alist of groupchats and nicknames
Keys are strings, the bare JID of the room.
Values are strings.")
(defvar jabber-muc-printers 'nil "\
List of functions that may be able to print part of a MUC message.
This gets prepended to `jabber-chat-printers', which see.")
(autoload 'jabber-muc-get-buffer "jabber-muc" "\
Return the chat buffer for chatroom GROUP.
Either a string or a buffer is returned, so use `get-buffer' or
`get-buffer-create'.
\(fn GROUP)" nil nil)
(autoload 'jabber-muc-private-get-buffer "jabber-muc" "\
Return the chat buffer for private chat with NICKNAME in GROUP.
Either a string or a buffer is returned, so use `get-buffer' or
`get-buffer-create'.
\(fn GROUP NICKNAME)" nil nil)
(autoload 'jabber-muc-vcard-get "jabber-muc" "\
Request vcard from chat with NICKNAME in GROUP.
\(fn JC GROUP NICKNAME)" t nil)
(autoload 'jabber-muc-message-p "jabber-muc" "\
Return non-nil if MESSAGE is a groupchat message.
That does not include private messages in a groupchat, but does
include groupchat invites.
\(fn MESSAGE)" nil nil)
(autoload 'jabber-muc-sender-p "jabber-muc" "\
Return non-nil if JID is a full JID of an MUC participant.
\(fn JID)" nil nil)
(autoload 'jabber-muc-private-message-p "jabber-muc" "\
Return non-nil if MESSAGE is a private message in a groupchat.
\(fn MESSAGE)" nil nil)
(register-definition-prefixes "jabber-muc" '("jabber-"))
;;;***
;;;### (autoloads nil "jabber-muc-nick-coloring" "jabber-muc-nick-coloring.el"
;;;;;; (0 0 0 0))
;;; Generated autoloads from jabber-muc-nick-coloring.el
(register-definition-prefixes "jabber-muc-nick-coloring" '("jabber-muc-"))
;;;***
;;;### (autoloads nil "jabber-muc-nick-completion" "jabber-muc-nick-completion.el"
;;;;;; (0 0 0 0))
;;; Generated autoloads from jabber-muc-nick-completion.el
(autoload 'jabber-muc-looks-like-personal-p "jabber-muc-nick-completion" "\
Return non-nil if jabber MESSAGE is addresed to me.
Optional argument GROUP to look.
\(fn MESSAGE &optional GROUP)" nil nil)
(register-definition-prefixes "jabber-muc-nick-completion" '("*jabber-muc-participant-last-speaking*" "jabber-" "try-expand-jabber-muc"))
;;;***
;;;### (autoloads nil "jabber-ourversion" "jabber-ourversion.el"
;;;;;; (0 0 0 0))
;;; Generated autoloads from jabber-ourversion.el
(register-definition-prefixes "jabber-ourversion" '("jabber-version"))
;;;***
;;;### (autoloads nil "jabber-ping" "jabber-ping.el" (0 0 0 0))
;;; Generated autoloads from jabber-ping.el
(register-definition-prefixes "jabber-ping" '("jabber-p"))
;;;***
;;;### (autoloads nil "jabber-presence" "jabber-presence.el" (0 0
;;;;;; 0 0))
;;; Generated autoloads from jabber-presence.el
(autoload 'jabber-send-presence "jabber-presence" "\
Set presence for all accounts.
\(fn SHOW STATUS PRIORITY)" t nil)
(autoload 'jabber-send-default-presence "jabber-presence" "\
Send default presence.
Default presence is specified by `jabber-default-show',
`jabber-default-status', and `jabber-default-priority'.
\(fn &optional IGNORE)" t nil)
(register-definition-prefixes "jabber-presence" '("jabber-"))
;;;***
;;;### (autoloads nil "jabber-private" "jabber-private.el" (0 0 0
;;;;;; 0))
;;; Generated autoloads from jabber-private.el
(autoload 'jabber-private-get "jabber-private" "\
Retrieve an item from private XML storage.
The item to retrieve is identified by NODE-NAME (a symbol) and
NAMESPACE (a string).
On success, SUCCESS-CALLBACK is called with JC and the retrieved
XML fragment.
On error, ERROR-CALLBACK is called with JC and the entire IQ
result.
\(fn JC NODE-NAME NAMESPACE SUCCESS-CALLBACK ERROR-CALLBACK)" nil nil)
(autoload 'jabber-private-set "jabber-private" "\
Store FRAGMENT in private XML storage.
SUCCESS-CALLBACK, SUCCESS-CLOSURE-DATA, ERROR-CALLBACK and
ERROR-CLOSURE-DATA are used as in `jabber-send-iq'.
\(fn JC FRAGMENT &optional SUCCESS-CALLBACK SUCCESS-CLOSURE-DATA ERROR-CALLBACK ERROR-CLOSURE-DATA)" nil nil)
(register-definition-prefixes "jabber-private" '("jabber-private-get-1"))
;;;***
;;;### (autoloads nil "jabber-ratpoison" "jabber-ratpoison.el" (0
;;;;;; 0 0 0))
;;; Generated autoloads from jabber-ratpoison.el
(register-definition-prefixes "jabber-ratpoison" '("jabber-ratpoison-message" "ratpoison"))
;;;***
;;;### (autoloads nil "jabber-register" "jabber-register.el" (0 0
;;;;;; 0 0))
;;; Generated autoloads from jabber-register.el
(register-definition-prefixes "jabber-register" '("jabber-"))
;;;***
;;;### (autoloads nil "jabber-roster" "jabber-roster.el" (0 0 0 0))
;;; Generated autoloads from jabber-roster.el
(autoload 'jabber-switch-to-roster-buffer "jabber-roster" "\
Switch to roster buffer.
Optional JC argument is ignored; it's there so this function can
be used in `jabber-post-connection-hooks'.
\(fn &optional JC)" t nil)
(autoload 'jabber-roster-update "jabber-roster" "\
Update roster, in memory and on display.
Add NEW-ITEMS, update CHANGED-ITEMS and remove DELETED-ITEMS, all
three being lists of JID symbols.
\(fn JC NEW-ITEMS CHANGED-ITEMS DELETED-ITEMS)" nil nil)
(register-definition-prefixes "jabber-roster" '("jabber-"))
;;;***
;;;### (autoloads nil "jabber-rtt" "jabber-rtt.el" (0 0 0 0))
;;; Generated autoloads from jabber-rtt.el
(eval-after-load "jabber-disco" '(jabber-disco-advertise-feature "urn:xmpp:rtt:0"))
(eval-after-load "jabber-core" '(add-to-list 'jabber-message-chain #'jabber-rtt-handle-message t))
(autoload 'jabber-rtt-handle-message "jabber-rtt" "\
\(fn JC XML-DATA)" nil nil)
(autoload 'jabber-rtt-send-mode "jabber-rtt" "\
Show text to recipient as it is being typed.
This lets the recipient see every change made to the message up
until it's sent. The recipient's client needs to implement
XEP-0301, In-Band Real Time Text.
If called interactively, toggle `Jabber-Rtt-Send mode'. If the
prefix argument is positive, enable the mode, and if it is zero
or negative, disable the mode.
If called from Lisp, toggle the mode if ARG is `toggle'. Enable
the mode if ARG is nil, omitted, or is a positive number.
Disable the mode if ARG is a negative number.
The mode's hook is called both when the mode is enabled and when
it is disabled.
\(fn &optional ARG)" t nil)
(register-definition-prefixes "jabber-rtt" '("jabber-rtt-"))
;;;***
;;;### (autoloads nil "jabber-sasl" "jabber-sasl.el" (0 0 0 0))
;;; Generated autoloads from jabber-sasl.el
(register-definition-prefixes "jabber-sasl" '("jabber-sasl-"))
;;;***
;;;### (autoloads nil "jabber-sawfish" "jabber-sawfish.el" (0 0 0
;;;;;; 0))
;;; Generated autoloads from jabber-sawfish.el
(register-definition-prefixes "jabber-sawfish" '("jabber-sawfish-display-" "sawfish"))
;;;***
;;;### (autoloads nil "jabber-screen" "jabber-screen.el" (0 0 0 0))
;;; Generated autoloads from jabber-screen.el
(register-definition-prefixes "jabber-screen" '("jabber-screen-message" "screen"))
;;;***
;;;### (autoloads nil "jabber-search" "jabber-search.el" (0 0 0 0))
;;; Generated autoloads from jabber-search.el
(register-definition-prefixes "jabber-search" '("jabber-"))
;;;***
;;;### (autoloads nil "jabber-si-client" "jabber-si-client.el" (0
;;;;;; 0 0 0))
;;; Generated autoloads from jabber-si-client.el
(register-definition-prefixes "jabber-si-client" '("jabber-si-initiate"))
;;;***
;;;### (autoloads nil "jabber-si-common" "jabber-si-common.el" (0
;;;;;; 0 0 0))
;;; Generated autoloads from jabber-si-common.el
(register-definition-prefixes "jabber-si-common" '("jabber-si-stream-methods"))
;;;***
;;;### (autoloads nil "jabber-si-server" "jabber-si-server.el" (0
;;;;;; 0 0 0))
;;; Generated autoloads from jabber-si-server.el
(register-definition-prefixes "jabber-si-server" '("jabber-si-pro"))
;;;***
;;;### (autoloads nil "jabber-socks5" "jabber-socks5.el" (0 0 0 0))
;;; Generated autoloads from jabber-socks5.el
(register-definition-prefixes "jabber-socks5" '("jabber-socks5"))
;;;***
;;;### (autoloads nil "jabber-time" "jabber-time.el" (0 0 0 0))
;;; Generated autoloads from jabber-time.el
(register-definition-prefixes "jabber-time" '("jabber-"))
;;;***
;;;### (autoloads nil "jabber-tmux" "jabber-tmux.el" (0 0 0 0))
;;; Generated autoloads from jabber-tmux.el
(register-definition-prefixes "jabber-tmux" '("jabber-tmux-message" "tmux"))
;;;***
;;;### (autoloads nil "jabber-truncate" "jabber-truncate.el" (0 0
;;;;;; 0 0))
;;; Generated autoloads from jabber-truncate.el
(register-definition-prefixes "jabber-truncate" '("jabber-"))
;;;***
;;;### (autoloads nil "jabber-util" "jabber-util.el" (0 0 0 0))
;;; Generated autoloads from jabber-util.el
(register-definition-prefixes "jabber-util" '("jabber-" "string>-numerical" "url-xmpp"))
;;;***
;;;### (autoloads nil "jabber-vcard" "jabber-vcard.el" (0 0 0 0))
;;; Generated autoloads from jabber-vcard.el
(register-definition-prefixes "jabber-vcard" '("jabber-vcard-"))
;;;***
;;;### (autoloads nil "jabber-vcard-avatars" "jabber-vcard-avatars.el"
;;;;;; (0 0 0 0))
;;; Generated autoloads from jabber-vcard-avatars.el
(register-definition-prefixes "jabber-vcard-avatars" '("jabber-vcard-avatars-"))
;;;***
;;;### (autoloads nil "jabber-version" "jabber-version.el" (0 0 0
;;;;;; 0))
;;; Generated autoloads from jabber-version.el
(register-definition-prefixes "jabber-version" '("jabber-"))
;;;***
;;;### (autoloads nil "jabber-watch" "jabber-watch.el" (0 0 0 0))
;;; Generated autoloads from jabber-watch.el
(register-definition-prefixes "jabber-watch" '("jabber-"))
;;;***
;;;### (autoloads nil "jabber-widget" "jabber-widget.el" (0 0 0 0))
;;; Generated autoloads from jabber-widget.el
(register-definition-prefixes "jabber-widget" '("jabber-" "jid-complete"))
;;;***
;;;### (autoloads nil "jabber-wmii" "jabber-wmii.el" (0 0 0 0))
;;; Generated autoloads from jabber-wmii.el
(register-definition-prefixes "jabber-wmii" '("jabber-wmii-" "wmii"))
;;;***
;;;### (autoloads nil "jabber-xmessage" "jabber-xmessage.el" (0 0
;;;;;; 0 0))
;;; Generated autoloads from jabber-xmessage.el
(register-definition-prefixes "jabber-xmessage" '("jabber-xmessage-" "xmessage"))
;;;***
;;;### (autoloads nil "jabber-xml" "jabber-xml.el" (0 0 0 0))
;;; Generated autoloads from jabber-xml.el
(register-definition-prefixes "jabber-xml" '("jabber-"))
;;;***
;;;### (autoloads nil nil ("jabber-festival.el" "jabber-notifications.el"
;;;;;; "jabber-osd.el" "jabber-pkg.el") (0 0 0 0))
;;;***
;; Local Variables:
;; version-control: never
;; no-byte-compile: t
;; no-update-autoloads: t
;; coding: utf-8
;; End:
;;; jabber-autoloads.el ends here

View File

@ -1,234 +0,0 @@
;;; jabber-avatar.el --- generic functions for avatars
;; Copyright (C) 2006, 2007, 2008 Magnus Henoch
;; Author: Magnus Henoch <mange@freemail.hu>
;; This file 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.
;; This file 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 GNU Emacs; see the file COPYING. If not, write to
;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
;; Boston, MA 02110-1301, USA.
;;; Commentary:
;; There are several methods for transporting avatars in Jabber
;; (JEP-0008, JEP-0084, JEP-0153). They all have in common that they
;; identify avatars by their SHA1 checksum, and (at least partially)
;; use Base64-encoded image data. Thus this library of support
;; functions for interpreting and caching avatars.
;; A contact with an avatar has the image in the avatar property of
;; the JID symbol. Use `jabber-avatar-set' to set it.
;;; Code:
(require 'mailcap)
(eval-when-compile (require 'cl))
;;;; Variables
(defgroup jabber-avatar nil
"Avatar related settings"
:group 'jabber)
(defcustom jabber-avatar-cache-directory
(locate-user-emacs-file "jabber-avatar-cache" ".jabber-avatars")
"Directory to use for cached avatars"
:group 'jabber-avatar
:type 'directory)
(defcustom jabber-avatar-verbose nil
"Display messages about irregularities with other people's avatars."
:group 'jabber-avatar
:type 'boolean)
(defcustom jabber-avatar-max-width 96
"Maximum width of avatars."
:group 'jabber-avatar
:type 'integer)
(defcustom jabber-avatar-max-height 96
"Maximum height of avatars."
:group 'jabber-avatar
:type 'integer)
;;;; Avatar data handling
(defstruct avatar sha1-sum mime-type url base64-data height width bytes)
(defun jabber-avatar-from-url (url)
"Construct an avatar structure from the given URL.
Retrieves the image to find info about it."
(with-current-buffer (let ((coding-system-for-read 'binary))
(url-retrieve-synchronously url))
(let* ((case-fold-search t)
(mime-type (ignore-errors
(search-forward-regexp "^content-type:[ \t]*\\(.*\\)$")
(match-string 1)))
(data (progn
(search-forward "\n\n")
(buffer-substring (point) (point-max)))))
(prog1
(jabber-avatar-from-data data nil mime-type)
(kill-buffer nil)))))
(defun jabber-avatar-from-file (filename)
"Construct an avatar structure from FILENAME."
(require 'mailcap)
(let ((data (with-temp-buffer
(insert-file-contents-literally filename)
(buffer-string)))
(mime-type (when (string-match "\\.[^.]+$" filename)
(mailcap-extension-to-mime (match-string 0 filename)))))
(jabber-avatar-from-data data nil mime-type)))
(defun jabber-avatar-from-base64-string (base64-string &optional mime-type)
"Construct an avatar stucture from BASE64-STRING.
If MIME-TYPE is not specified, try to find it from the image data."
(jabber-avatar-from-data nil base64-string mime-type))
(defun jabber-avatar-from-data (raw-data base64-string &optional mime-type)
"Construct an avatar structure from RAW-DATA and/or BASE64-STRING.
If either is not provided, it is computed.
If MIME-TYPE is not specified, try to find it from the image data."
(let* ((data (or raw-data (base64-decode-string base64-string)))
(bytes (length data))
(sha1-sum (sha1 data))
(base64-data (or base64-string (base64-encode-string raw-data)))
(type (or mime-type
(cdr (assq (get :type (cdr (condition-case nil
(jabber-create-image data nil t)
(error nil))))
'((png "image/png")
(jpeg "image/jpeg")
(gif "image/gif")))))))
(jabber-avatar-compute-size
(make-avatar :mime-type mime-type :sha1-sum sha1-sum :base64-data base64-data :bytes bytes))))
;; XXX: This function is based on an outdated version of JEP-0084.
;; (defun jabber-avatar-from-data-node (data-node)
;; "Construct an avatar structure from the given <data/> node."
;; (jabber-xml-let-attributes
;; (content-type id bytes height width) data-node
;; (let ((base64-data (car (jabber-xml-node-children data-node))))
;; (make-avatar :mime-type content-type :sha1-sum id :bytes bytes
;; :height height :width width :base64-data base64-data))))
(defun jabber-avatar-image (avatar)
"Create an image from AVATAR.
Return nil if images of this type are not supported."
(condition-case nil
(jabber-create-image (with-temp-buffer
(set-buffer-multibyte nil)
(insert (avatar-base64-data avatar))
(base64-decode-region (point-min) (point-max))
(buffer-string))
nil
t)
(error nil)))
(defun jabber-avatar-compute-size (avatar)
"Compute and set the width and height fields of AVATAR.
Return AVATAR."
;; image-size only works when there is a window system.
;; But display-graphic-p doesn't exist on XEmacs...
(let ((size (and (fboundp 'display-graphic-p)
(display-graphic-p)
(let ((image (jabber-avatar-image avatar)))
(and image
(image-size image t))))))
(when size
(setf (avatar-width avatar) (car size))
(setf (avatar-height avatar) (cdr size)))
avatar))
;;;; Avatar cache
(defun jabber-avatar-find-cached (sha1-sum)
"Return file name of cached image for avatar identified by SHA1-SUM.
If there is no cached image, return nil."
(let ((filename (expand-file-name sha1-sum jabber-avatar-cache-directory)))
(if (file-exists-p filename)
filename
nil)))
(defun jabber-avatar-cache (avatar)
"Cache the AVATAR."
(let* ((id (avatar-sha1-sum avatar))
(base64-data (avatar-base64-data avatar))
(mime-type (avatar-mime-type avatar))
(filename (expand-file-name id jabber-avatar-cache-directory)))
(unless (file-directory-p jabber-avatar-cache-directory)
(make-directory jabber-avatar-cache-directory t))
(if (file-exists-p filename)
(when jabber-avatar-verbose
(message "Caching avatar, but %s already exists" filename))
(with-temp-buffer
(let ((require-final-newline nil)
(coding-system-for-write 'binary))
(if (fboundp 'set-buffer-multibyte)
(set-buffer-multibyte nil))
(insert base64-data)
(base64-decode-region (point-min) (point-max))
(write-region (point-min) (point-max) filename nil 'silent))))))
;;;; Set avatar for contact
(defun jabber-avatar-set (jid avatar)
"Set the avatar of JID to be AVATAR.
JID is a string containing a bare JID.
AVATAR may be one of:
* An avatar structure.
* The SHA1 sum of a cached avatar.
* nil, meaning no avatar."
;; We want to optimize for the case of same avatar.
;; Loading an image is expensive, so do it lazily.
(let ((jid-symbol (jabber-jid-symbol jid))
image hash)
(cond
((avatar-p avatar)
(setq hash (avatar-sha1-sum avatar))
(setq image (lambda () (jabber-avatar-image avatar))))
((stringp avatar)
(setq hash avatar)
(setq image (lambda ()
(condition-case nil
(jabber-create-image (jabber-avatar-find-cached avatar))
(error nil)))))
(t
(setq hash nil)
(setq image #'ignore)))
(unless (string= hash (get jid-symbol 'avatar-hash))
(put jid-symbol 'avatar (funcall image))
(put jid-symbol 'avatar-hash hash)
(jabber-presence-update-roster jid-symbol))))
(defun jabber-create-image (file-or-data &optional type data-p)
"Create image, scaled down to jabber-avatar-max-width/height,
if width/height exceeds either of those, and ImageMagick is
available."
(let* ((image (create-image file-or-data type data-p))
(size (image-size image t))
(spec (cdr image)))
(when (and (functionp 'imagemagick-types)
(or (> (car size) jabber-avatar-max-width)
(> (cdr size) jabber-avatar-max-height)))
(plist-put spec :type 'imagemagick)
(plist-put spec :width jabber-avatar-max-width)
(plist-put spec :height jabber-avatar-max-height))
image))
(provide 'jabber-avatar)
;; arch-tag: 2405c3f8-8eaa-11da-826c-000a95c2fcd0

View File

@ -1,42 +0,0 @@
;; jabber-awesome.el - emacs-jabber interface to awesome and naughty
;; Copyright (C) 2009 - Evgenii Terechkov - evg@altlinux.org
;; This file is a part of jabber.el.
;; 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
(eval-when-compile (require 'jabber-alert))
(defcustom jabber-awesome-args ", timeout=5"
"Additional args to naughty."
:type 'string
:group 'jabber-alerts)
(defun jabber-awesome-message (text &optional title)
"Show MSG in Awesome"
;; Possible errors include not finding the awesome binary.
(condition-case e
(let ((process-connection-type))
(shell-command-to-string (format "echo 'naughty.notify({text = \"%s\" %s})' | awesome-client -"
(or title text) jabber-awesome-args))
)
(error nil)))
(define-jabber-alert awesome "Show a message through the Awesome window manager"
'jabber-awesome-message)
(define-personal-jabber-alert jabber-muc-awesome)
(provide 'jabber-awesome)

View File

@ -1,248 +0,0 @@
;; jabber-bookmarks.el - bookmarks according to XEP-0048
;; Copyright (C) 2007, 2008 - Magnus Henoch - mange@freemail.hu
;; This file is a part of jabber.el.
;; 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
(require 'jabber-private)
(require 'jabber-widget)
(require 'cl)
(defvar jabber-bookmarks (make-hash-table :test 'equal)
"Mapping from full JIDs to bookmarks.
Bookmarks are what has been retrieved from the server, as list of
XML elements. This is nil if bookmarks have not been retrieved,
and t if no bookmarks where found.")
;;;###autoload
(defun jabber-get-conference-data (jc conference-jid cont &optional key)
"Get bookmark data for CONFERENCE-JID.
KEY may be nil or one of :name, :autojoin, :nick and :password.
If KEY is nil, a plist containing the above keys is returned.
CONT is called when the result is available, with JC and the
result as arguments. If CONT is nil, return the requested data
immediately, and return nil if it is not in the cache."
(if (null cont)
(let ((cache (jabber-get-bookmarks-from-cache jc)))
(if (and cache (listp cache))
(jabber-get-conference-data-internal
cache conference-jid key)))
(jabber-get-bookmarks
jc
(lexical-let ((conference-jid conference-jid)
(key key)
(cont cont))
(lambda (jc result)
(let ((entry (jabber-get-conference-data-internal result conference-jid key)))
(funcall cont jc entry)))))))
(defun jabber-get-conference-data-internal (result conference-jid key)
(let ((entry (dolist (node result)
(when (and (eq (jabber-xml-node-name node) 'conference)
(string= (jabber-xml-get-attribute node 'jid) conference-jid))
(return (jabber-parse-conference-bookmark node))))))
(if key
(plist-get entry key)
entry)))
;;;###autoload
(defun jabber-parse-conference-bookmark (node)
"Convert a <conference/> tag into a plist.
The plist may contain the keys :jid, :name, :autojoin,
:nick and :password."
(when (eq (jabber-xml-node-name node) 'conference)
(list :jid (jabber-xml-get-attribute node 'jid)
:name (jabber-xml-get-attribute node 'name)
:autojoin (member (jabber-xml-get-attribute node 'autojoin)
'("true" "1"))
:nick (car (jabber-xml-node-children
(car (jabber-xml-get-children node 'nick))))
:password (car (jabber-xml-node-children
(car (jabber-xml-get-children node 'password)))))))
;;;###autoload
(defun jabber-get-bookmarks (jc cont &optional refresh)
"Retrieve bookmarks (if needed) and call CONT.
Arguments to CONT are JC and the bookmark list. CONT will be
called as the result of a filter function or a timer.
If REFRESH is non-nil, always fetch bookmarks."
(let ((bookmarks (gethash (jabber-connection-bare-jid jc) jabber-bookmarks)))
(if (and (not refresh) bookmarks)
(run-with-timer 0 nil cont jc (when (listp bookmarks) bookmarks))
(lexical-let* ((cont cont)
(callback (lambda (jc result) (jabber-get-bookmarks-1 jc result cont))))
(jabber-private-get jc 'storage "storage:bookmarks"
callback callback)))))
(defun jabber-get-bookmarks-1 (jc result cont)
(let ((my-jid (jabber-connection-bare-jid jc))
(value
(if (eq (jabber-xml-node-name result) 'storage)
(or (jabber-xml-node-children result) t)
t)))
(puthash my-jid value jabber-bookmarks)
(funcall cont jc (when (listp value) value))))
;;;###autoload
(defun jabber-get-bookmarks-from-cache (jc)
"Return cached bookmarks for JC.
If bookmarks have not yet been fetched by `jabber-get-bookmarks',
return nil."
(gethash (jabber-connection-bare-jid jc) jabber-bookmarks))
(defun jabber-set-bookmarks (jc bookmarks &optional callback)
"Set bookmarks to BOOKMARKS, which is a list of XML elements.
If CALLBACK is non-nil, call it with JC and t or nil as arguments
on success or failure, respectively."
(unless callback
(setq callback #'ignore))
(jabber-private-set
jc
`(storage ((xmlns . "storage:bookmarks"))
,@bookmarks)
callback t
callback nil))
;;;###autoload
(defun jabber-edit-bookmarks (jc)
"Create a buffer for editing bookmarks interactively."
(interactive (list (jabber-read-account)))
(jabber-get-bookmarks jc 'jabber-edit-bookmarks-1 t))
(defun jabber-edit-bookmarks-1 (jc bookmarks)
(setq bookmarks
(mapcar
(lambda (e)
(case (jabber-xml-node-name e)
(url
(list 'url (or (jabber-xml-get-attribute e 'url) "")
(or (jabber-xml-get-attribute e 'name) "")))
(conference
(list 'conference
(or (jabber-xml-get-attribute e 'jid) "")
(or (jabber-xml-get-attribute e 'name) "")
(not (not (member (jabber-xml-get-attribute e 'autojoin)
'("true" "1"))))
(or (jabber-xml-path e '(nick "")) "")
(or (jabber-xml-path e '(password "")) "")))))
bookmarks))
(setq bookmarks (delq nil bookmarks))
(with-current-buffer (get-buffer-create "Edit bookmarks")
(jabber-init-widget-buffer nil)
(setq jabber-buffer-connection jc)
(widget-insert (jabber-propertize (concat "Edit bookmarks for "
(jabber-connection-bare-jid jc))
'face 'jabber-title-large)
"\n\n")
(when (or (bound-and-true-p jabber-muc-autojoin)
(bound-and-true-p jabber-muc-default-nicknames))
(widget-insert "The variables `jabber-muc-autojoin' and/or `jabber-muc-default-nicknames'\n"
"contain values. They are only available to jabber.el on this machine.\n"
"You may want to import them into your bookmarks, to make them available\n"
"to any client on any machine.\n")
(widget-create 'push-button :notify 'jabber-bookmarks-import "Import values from variables")
(widget-insert "\n\n"))
(push (cons 'bookmarks
(widget-create
'(repeat
:tag "Bookmarks"
(choice
(list :tag "Conference"
(const :format "" conference)
(string :tag "JID") ;XXX: jid widget type?
(string :tag "Name")
(checkbox :tag "Autojoin" :format "%[%v%] Autojoin?\n")
(string :tag "Nick") ;or nil?
(string :tag "Password") ;or nil?
)
(list :tag "URL"
(const :format "" url)
(string :tag "URL")
(string :tag "Name"))))
:value bookmarks))
jabber-widget-alist)
(widget-insert "\n")
(widget-create 'push-button :notify 'jabber-bookmarks-submit "Submit")
(widget-setup)
(widget-minor-mode 1)
(switch-to-buffer (current-buffer))
(goto-char (point-min))))
(defun jabber-bookmarks-submit (&rest ignore)
(let ((bookmarks (widget-value (cdr (assq 'bookmarks jabber-widget-alist)))))
(setq bookmarks
(mapcar
(lambda (entry)
(case (car entry)
(url
(destructuring-bind (symbol url name) entry
`(url ((url . ,url)
(name . ,name)))))
(conference
(destructuring-bind (symbol jid name autojoin nick password)
entry
`(conference ((jid . ,jid)
(name . ,name)
(autojoin . ,(if autojoin
"1"
"0")))
,@(unless (zerop (length nick))
`((nick () ,nick)))
,@(unless (zerop (length password))
`((password () ,password))))))))
bookmarks))
(remhash (jabber-connection-bare-jid jabber-buffer-connection) jabber-bookmarks)
(jabber-private-set
jabber-buffer-connection
`(storage ((xmlns . "storage:bookmarks"))
,@bookmarks)
'jabber-report-success "Storing bookmarks"
'jabber-report-success "Storing bookmarks")))
(defun jabber-bookmarks-import (&rest ignore)
(let* ((value (widget-value (cdr (assq 'bookmarks jabber-widget-alist))))
(conferences (mapcar
'cdr
(remove-if-not
(lambda (entry)
(eq (car entry) 'conference))
value))))
(dolist (default-nickname jabber-muc-default-nicknames)
(destructuring-bind (muc-jid . nick) default-nickname
(let ((entry (assoc muc-jid conferences)))
(if entry
(setf (fourth entry) nick)
(setq entry (list muc-jid "" nil nick ""))
(push entry conferences)
(push (cons 'conference entry) value)))))
(dolist (autojoin jabber-muc-autojoin)
(let ((entry (assoc autojoin conferences)))
(if entry
(setf (third entry) t)
(setq entry (list autojoin "" t "" ""))
(push (cons 'conference entry) value))))
(widget-value-set (cdr (assq 'bookmarks jabber-widget-alist)) value)
(widget-setup)))
(provide 'jabber-bookmarks)
;; arch-tag: a7d6f862-bac0-11db-831f-000a95c2fcd0

View File

@ -1,100 +0,0 @@
;; jabber-browse.el - jabber browsing by JEP-0011
;; Copyright (C) 2002, 2003, 2004 - tom berger - object@intelectronica.net
;; Copyright (C) 2003, 2004 - Magnus Henoch - mange@freemail.hu
;; This file is a part of jabber.el.
;; 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
(require 'jabber-iq)
(require 'jabber-xml)
(require 'jabber-util)
;; jabber.el can perform browse requests, but will not answer them.
(add-to-list 'jabber-jid-info-menu
(cons "Send browse query" 'jabber-get-browse))
(defun jabber-get-browse (jc to)
"send a browse infoquery request to someone"
(interactive (list (jabber-read-account)
(jabber-read-jid-completing "browse: " nil nil nil nil t)))
(jabber-send-iq jc to
"get"
'(query ((xmlns . "jabber:iq:browse")))
#'jabber-process-data #'jabber-process-browse
#'jabber-process-data "Browse failed"))
;; called from jabber-process-data
(defun jabber-process-browse (jc xml-data)
"Handle results from jabber:iq:browse requests."
(dolist (item (jabber-xml-node-children xml-data))
(when (and (listp item)
(not (eq (jabber-xml-node-name item) 'ns)))
(let ((jid (jabber-xml-get-attribute item 'jid))
(beginning (point)))
(cond
((or
(eq (jabber-xml-node-name item) 'user)
(string= (jabber-xml-get-attribute item 'category) "user"))
(insert (jabber-propertize "$ USER"
'face 'jabber-title-medium)
"\n\n"))
((or
(eq (jabber-xml-node-name item) 'service)
(string= (jabber-xml-get-attribute item 'category) "service"))
(insert (jabber-propertize "* SERVICE"
'face 'jabber-title-medium)
"\n\n"))
((or
(eq (jabber-xml-node-name item) 'conference)
(string= (jabber-xml-get-attribute item 'category) "conference"))
(insert (jabber-propertize "@ CONFERENCE"
'face 'jabber-title-medium)
"\n\n"))
(t
;; So far I've seen "server" and "directory", both in the node-name.
;; Those are actually service disco categories, but jabberd 2 seems
;; to use them for browse results as well. It's not right (as in
;; JEP-0011), but it's reasonable.
(let ((category (jabber-xml-get-attribute item 'category)))
(if (= (length category) 0)
(setq category (jabber-xml-node-name item)))
(insert (jabber-propertize (format "! OTHER: %s" category)
'face 'jabber-title-medium)
"\n\n"))))
(dolist (attr '((type . "Type:\t\t")
(jid . "JID:\t\t")
(name . "Name:\t\t")
(version . "Version:\t")))
(let ((data (jabber-xml-get-attribute item (car attr))))
(if (> (length data) 0)
(insert (cdr attr) data "\n"))))
(dolist (ns (jabber-xml-get-children item 'ns))
(if (stringp (car (jabber-xml-node-children ns)))
(insert "Namespace:\t" (car (jabber-xml-node-children ns)) "\n")))
(insert "\n")
(put-text-property beginning (point) 'jabber-jid jid)
(put-text-property beginning (point) 'jabber-account jc)
;; XXX: Is this kind of recursion really needed?
(if (listp (car (jabber-xml-node-children item)))
(jabber-process-browse jc item))))))
(provide 'jabber-browse)
;;; arch-tag: be01ab34-96eb-4fcb-aa35-a0d3e6c446c3

View File

@ -1,683 +0,0 @@
;; jabber-chat.el - one-to-one chats
;; Copyright (C) 2005, 2007, 2008 - Magnus Henoch - mange@freemail.hu
;; This file is a part of jabber.el.
;; 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
(require 'jabber-core)
(require 'jabber-chatbuffer)
(require 'jabber-history)
(require 'jabber-menu) ;we need jabber-jid-chat-menu
(require 'ewoc)
(eval-when-compile (require 'cl))
(defgroup jabber-chat nil "chat display options"
:group 'jabber)
(defcustom jabber-chat-buffer-format "*-jabber-chat-%n-*"
"The format specification for the name of chat buffers.
These fields are available (all are about the person you are chatting
with):
%n Nickname, or JID if no nickname set
%j Bare JID (without resource)
%r Resource"
:type 'string
:group 'jabber-chat)
(defcustom jabber-chat-header-line-format
'("" (jabber-chat-buffer-show-avatar
(:eval
(let ((buddy (jabber-jid-symbol jabber-chatting-with)))
(jabber-propertize " "
'display (get buddy 'avatar)))))
(:eval (jabber-jid-displayname jabber-chatting-with))
"\t" (:eval (let ((buddy (jabber-jid-symbol jabber-chatting-with)))
(propertize
(or
(cdr (assoc (get buddy 'show) jabber-presence-strings))
(get buddy 'show))
'face
(or (cdr (assoc (get buddy 'show) jabber-presence-faces))
'jabber-roster-user-online))))
"\t" (:eval (jabber-fix-status (get (jabber-jid-symbol jabber-chatting-with) 'status)))
"\t" jabber-events-message ;see jabber-events.el
"\t" jabber-chatstates-message) ;see jabber-chatstates.el
"The specification for the header line of chat buffers.
The format is that of `mode-line-format' and `header-line-format'."
:type 'sexp
:group 'jabber-chat)
(defcustom jabber-chat-buffer-show-avatar t
"Show avatars in header line of chat buffer?
This variable might not take effect if you have changed
`jabber-chat-header-line-format'."
:type 'boolean
:group 'jabber-chat)
(defcustom jabber-chat-time-format "%H:%M"
"The format specification for instant messages in the chat buffer.
See also `jabber-chat-delayed-time-format'.
See `format-time-string' for valid values."
:type 'string
:group 'jabber-chat)
(defcustom jabber-chat-delayed-time-format "%Y-%m-%d %H:%M"
"The format specification for delayed messages in the chat buffer.
See also `jabber-chat-time-format'.
See `format-time-string' for valid values."
:type 'string
:group 'jabber-chat)
(defcustom jabber-print-rare-time t
"Non-nil means to print \"rare time\" indications in chat buffers.
The default settings tell every new hour."
:type 'boolean
:group 'jabber-chat)
(defcustom jabber-rare-time-format "%a %e %b %Y %H:00"
"The format specification for the rare time information.
Rare time information will be printed whenever the current time,
formatted according to this string, is different to the last
rare time printed."
:type 'string
:group 'jabber-chat)
(defface jabber-rare-time-face
'((t (:foreground "darkgreen" :underline t)))
"face for displaying the rare time info"
:group 'jabber-chat)
(defcustom jabber-chat-local-prompt-format "[%t] %n> "
"The format specification for lines you type in the chat buffer.
These fields are available:
%t Time, formatted according to `jabber-chat-time-format'
or `jabber-chat-delayed-time-format'
%u Username
%n Nickname (obsolete, same as username)
%r Resource
%j Bare JID (without resource)"
:type 'string
:group 'jabber-chat)
(defcustom jabber-chat-foreign-prompt-format "[%t] %n> "
"The format specification for lines others type in the chat buffer.
These fields are available:
%t Time, formatted according to `jabber-chat-time-format'
or `jabber-chat-delayed-time-format'
%n Nickname, or JID if no nickname set
%u Username
%r Resource
%j Bare JID (without resource)"
:type 'string
:group 'jabber-chat)
(defcustom jabber-chat-system-prompt-format "[%t] *** "
"The format specification for lines from the system or that are special in the chat buffer."
:type 'string
:group 'jabber-chat)
(defface jabber-chat-prompt-local
'((t (:foreground "blue" :weight bold)))
"face for displaying the chat prompt for what you type in"
:group 'jabber-chat)
(defface jabber-chat-prompt-foreign
'((t (:foreground "red" :weight bold)))
"face for displaying the chat prompt for what they send"
:group 'jabber-chat)
(defface jabber-chat-prompt-system
'((t (:foreground "green" :weight bold)))
"face used for system and special messages"
:group 'jabber-chat)
(defface jabber-chat-text-local '((t ()))
"Face used for text you write"
:group 'jabber-chat)
(defface jabber-chat-text-foreign '((t ()))
"Face used for text others write"
:group 'jabber-chat)
(defface jabber-chat-error
'((t (:foreground "red" :weight bold)))
"Face used for error messages"
:group 'jabber-chat)
;;;###autoload
(defvar jabber-chatting-with nil
"JID of the person you are chatting with")
(defvar jabber-chat-printers '(jabber-chat-print-subject
jabber-chat-print-body
jabber-chat-print-url
jabber-chat-goto-address)
"List of functions that may be able to print part of a message.
Each function receives these arguments:
XML-DATA The entire message stanza
WHO :local or :foreign, for sent or received stanza, respectively
MODE :insert or :printp. For :insert, insert text at point.
For :printp, return non-nil if function would insert text.")
(defvar jabber-body-printers '(jabber-chat-normal-body)
"List of functions that may be able to print a body for a message.
Each function receives these arguments:
XML-DATA The entire message stanza
WHO :local, :foreign or :error
MODE :insert or :printp. For :insert, insert text at point.
For :printp, return non-nil if function would insert text.
These functions are called in order, until one of them returns
non-nil.
Add a function to the beginning of this list if the tag it handles
replaces the contents of the <body/> tag.")
(defvar jabber-chat-send-hooks nil
"List of functions called when a chat message is sent.
The arguments are the text to send, and the id attribute of the
message.
The functions should return a list of XML nodes they want to be
added to the outgoing message.")
(defvar jabber-chat-earliest-backlog nil
"Float-time of earliest backlog entry inserted into buffer.
nil if no backlog has been inserted.")
;;;###autoload
(defun jabber-chat-get-buffer (chat-with)
"Return the chat buffer for chatting with CHAT-WITH (bare or full JID).
Either a string or a buffer is returned, so use `get-buffer' or
`get-buffer-create'."
(format-spec jabber-chat-buffer-format
(list
(cons ?n (jabber-jid-displayname chat-with))
(cons ?j (jabber-jid-user chat-with))
(cons ?r (or (jabber-jid-resource chat-with) "")))))
(defun jabber-chat-create-buffer (jc chat-with)
"Prepare a buffer for chatting with CHAT-WITH.
This function is idempotent."
(with-current-buffer (get-buffer-create (jabber-chat-get-buffer chat-with))
(unless (eq major-mode 'jabber-chat-mode)
(jabber-chat-mode jc #'jabber-chat-pp)
(make-local-variable 'jabber-chatting-with)
(setq jabber-chatting-with chat-with)
(setq jabber-send-function 'jabber-chat-send)
(setq header-line-format jabber-chat-header-line-format)
(make-local-variable 'jabber-chat-earliest-backlog)
;; insert backlog
(when (null jabber-chat-earliest-backlog)
(let ((backlog-entries (jabber-history-backlog chat-with)))
(if (null backlog-entries)
(setq jabber-chat-earliest-backlog (jabber-float-time))
(setq jabber-chat-earliest-backlog
(jabber-float-time (jabber-parse-time
(aref (car backlog-entries) 0))))
(mapc 'jabber-chat-insert-backlog-entry (nreverse backlog-entries))))))
;; Make sure the connection variable is up to date.
(setq jabber-buffer-connection jc)
(current-buffer)))
(defun jabber-chat-insert-backlog-entry (msg)
"Insert backlog entry MSG at beginning of buffer."
;; Rare timestamps are especially important in backlog. We risk
;; having superfluous timestamps if we just add before each backlog
;; entry.
(let* ((message-time (jabber-parse-time (aref msg 0)))
(fake-stanza `(message ((from . ,(aref msg 2)))
(body nil ,(aref msg 4))
(x ((xmlns . "jabber:x:delay")
(stamp . ,(jabber-encode-legacy-time message-time))))))
(node-data (list (if (string= (aref msg 1) "in") :foreign :local)
fake-stanza :delayed t)))
;; Insert after existing rare timestamp?
(if (and jabber-print-rare-time
(ewoc-nth jabber-chat-ewoc 0)
(eq (car (ewoc-data (ewoc-nth jabber-chat-ewoc 0))) :rare-time)
(not (jabber-rare-time-needed message-time (cadr (ewoc-data (ewoc-nth jabber-chat-ewoc 0))))))
(ewoc-enter-after jabber-chat-ewoc (ewoc-nth jabber-chat-ewoc 0) node-data)
;; Insert first.
(ewoc-enter-first jabber-chat-ewoc node-data)
(when jabber-print-rare-time
(ewoc-enter-first jabber-chat-ewoc (list :rare-time message-time))))))
(add-to-list 'jabber-jid-chat-menu
(cons "Display more context" 'jabber-chat-display-more-backlog))
(defun jabber-chat-display-more-backlog (how-many)
"Display more context. HOW-MANY is number of messages. Specify 0 to display all messages."
(interactive "nHow many more messages (Specify 0 to display all)? ")
(let* ((inhibit-read-only t)
(jabber-backlog-days nil)
(jabber-backlog-number (if (= how-many 0) t how-many))
(backlog-entries (jabber-history-backlog
jabber-chatting-with jabber-chat-earliest-backlog)))
(when backlog-entries
(setq jabber-chat-earliest-backlog
(jabber-float-time (jabber-parse-time
(aref (car backlog-entries) 0))))
(save-excursion
(goto-char (point-min))
(mapc 'jabber-chat-insert-backlog-entry (nreverse backlog-entries))))))
(add-to-list 'jabber-message-chain 'jabber-process-chat)
(defun jabber-process-chat (jc xml-data)
"If XML-DATA is a one-to-one chat message, handle it as such."
;; For now, everything that is not a public MUC message is
;; potentially a 1to1 chat message.
(when (not (jabber-muc-message-p xml-data))
;; Note that we handle private MUC messages here.
(let ((from (jabber-xml-get-attribute xml-data 'from))
(error-p (jabber-xml-get-children xml-data 'error))
(body-text (car (jabber-xml-node-children
(car (jabber-xml-get-children
xml-data 'body))))))
;; First check if we would output anything for this stanza.
(when (or error-p
(run-hook-with-args-until-success 'jabber-chat-printers xml-data :foreign :printp))
;; If so, create chat buffer, if necessary...
(with-current-buffer (if (jabber-muc-sender-p from)
(jabber-muc-private-create-buffer
jc
(jabber-jid-user from)
(jabber-jid-resource from))
(jabber-chat-create-buffer jc from))
;; ...add the message to the ewoc...
(let ((node
(ewoc-enter-last jabber-chat-ewoc (list (if error-p :error :foreign) xml-data :time (current-time)))))
(jabber-maybe-print-rare-time node))
;; ...and call alert hooks.
(dolist (hook '(jabber-message-hooks jabber-alert-message-hooks))
(run-hook-with-args hook
from (current-buffer) body-text
(funcall jabber-alert-message-function
from (current-buffer) body-text))))))))
(defun jabber-chat-send (jc body)
"Send BODY through connection JC, and display it in chat buffer."
;; Build the stanza...
(let* ((id (apply 'format "emacs-msg-%d.%d.%d" (current-time)))
(stanza-to-send `(message
((to . ,jabber-chatting-with)
(type . "chat")
(id . ,id))
(body () ,body))))
;; ...add additional elements...
;; TODO: Once we require Emacs 24.1, use `run-hook-wrapped' instead.
;; That way we don't need to eliminate the "local hook" functionality
;; here.
(dolist (hook jabber-chat-send-hooks)
(if (eq hook t)
;; Local hook referring to global...
(when (local-variable-p 'jabber-chat-send-hooks)
(dolist (global-hook (default-value 'jabber-chat-send-hooks))
(nconc stanza-to-send (funcall global-hook body id))))
(nconc stanza-to-send (funcall hook body id))))
;; ...display it, if it would be displayed.
(when (run-hook-with-args-until-success 'jabber-chat-printers stanza-to-send :local :printp)
(jabber-maybe-print-rare-time
(ewoc-enter-last jabber-chat-ewoc (list :local stanza-to-send :time (current-time)))))
;; ...and send it...
(jabber-send-sexp jc stanza-to-send)))
(defun jabber-chat-pp (data)
"Pretty-print a <message/> stanza.
\(car data) is either :local, :foreign, :error or :notice.
\(cadr data) is the <message/> stanza.
This function is used as an ewoc prettyprinter."
(let* ((beg (point))
(original-timestamp (when (listp (cadr data))
(jabber-message-timestamp (cadr data))))
(internal-time
(plist-get (cddr data) :time))
(body (ignore-errors (car
(jabber-xml-node-children
(car
(jabber-xml-get-children (cadr data) 'body))))))
(/me-p
(and (> (length body) 4)
(string= (substring body 0 4) "/me "))))
;; Print prompt...
(let ((delayed (or original-timestamp (plist-get (cddr data) :delayed)))
(prompt-start (point)))
(case (car data)
(:local
(jabber-chat-self-prompt (or original-timestamp internal-time)
delayed
/me-p))
(:foreign
(if (and (listp (cadr data))
(jabber-muc-private-message-p (cadr data)))
(jabber-muc-private-print-prompt (cadr data))
;; For :error and :notice, this might be a string... beware
(jabber-chat-print-prompt (when (listp (cadr data)) (cadr data))
(or original-timestamp internal-time)
delayed
/me-p)))
((:error :notice :subscription-request)
(jabber-chat-system-prompt (or original-timestamp internal-time)))
(:muc-local
(jabber-muc-print-prompt (cadr data) t /me-p))
(:muc-foreign
(jabber-muc-print-prompt (cadr data) nil /me-p))
((:muc-notice :muc-error)
(jabber-muc-system-prompt)))
(put-text-property prompt-start (point) 'field 'jabber-prompt))
;; ...and body
(case (car data)
((:local :foreign)
(run-hook-with-args 'jabber-chat-printers (cadr data) (car data) :insert))
((:muc-local :muc-foreign)
(let ((printers (append jabber-muc-printers jabber-chat-printers)))
(run-hook-with-args 'printers (cadr data) (car data) :insert)))
((:error :muc-error)
(if (stringp (cadr data))
(insert (jabber-propertize (cadr data) 'face 'jabber-chat-error))
(jabber-chat-print-error (cadr data))))
((:notice :muc-notice)
(insert (cadr data)))
(:rare-time
(insert (jabber-propertize (format-time-string jabber-rare-time-format (cadr data))
'face 'jabber-rare-time-face)))
(:subscription-request
(insert "This user requests subscription to your presence.\n")
(when (and (stringp (cadr data)) (not (zerop (length (cadr data)))))
(insert "Message: " (cadr data) "\n"))
(insert "Accept?\n\n")
(flet ((button
(text action)
(if (fboundp 'insert-button)
(insert-button text 'action action)
;; simple button replacement
(let ((keymap (make-keymap)))
(define-key keymap "\r" action)
(insert (jabber-propertize text 'keymap keymap 'face 'highlight))))
(insert "\t")))
(button "Mutual" 'jabber-subscription-accept-mutual)
(button "One-way" 'jabber-subscription-accept-one-way)
(button "Decline" 'jabber-subscription-decline))))
(when jabber-chat-fill-long-lines
(save-restriction
(narrow-to-region beg (point))
(jabber-chat-buffer-fill-long-lines)))
(put-text-property beg (point) 'read-only t)
(put-text-property beg (point) 'front-sticky t)
(put-text-property beg (point) 'rear-nonsticky t)))
(defun jabber-rare-time-needed (time1 time2)
"Return non-nil if a timestamp should be printed between TIME1 and TIME2."
(not (string= (format-time-string jabber-rare-time-format time1)
(format-time-string jabber-rare-time-format time2))))
(defun jabber-maybe-print-rare-time (node)
"Print rare time before NODE, if appropriate."
(let* ((prev (ewoc-prev jabber-chat-ewoc node))
(data (ewoc-data node))
(prev-data (when prev (ewoc-data prev))))
(flet ((entry-time (entry)
(or (when (listp (cadr entry))
(jabber-message-timestamp (cadr entry)))
(plist-get (cddr entry) :time))))
(when (and jabber-print-rare-time
(or (null prev)
(jabber-rare-time-needed (entry-time prev-data)
(entry-time data))))
(ewoc-enter-before jabber-chat-ewoc node
(list :rare-time (entry-time data)))))))
(defun jabber-chat-print-prompt (xml-data timestamp delayed dont-print-nick-p)
"Print prompt for received message in XML-DATA.
TIMESTAMP is the timestamp to print, or nil to get it
from a jabber:x:delay element.
If DELAYED is true, print long timestamp
\(`jabber-chat-delayed-time-format' as opposed to
`jabber-chat-time-format').
If DONT-PRINT-NICK-P is true, don't include nickname."
(let ((from (jabber-xml-get-attribute xml-data 'from))
(timestamp (or timestamp (jabber-message-timestamp xml-data))))
(insert (jabber-propertize
(format-spec jabber-chat-foreign-prompt-format
(list
(cons ?t (format-time-string
(if delayed
jabber-chat-delayed-time-format
jabber-chat-time-format)
timestamp))
(cons ?n (if dont-print-nick-p "" (jabber-jid-displayname from)))
(cons ?u (or (jabber-jid-username from) from))
(cons ?r (jabber-jid-resource from))
(cons ?j (jabber-jid-user from))))
'face 'jabber-chat-prompt-foreign
'help-echo
(concat (format-time-string "On %Y-%m-%d %H:%M:%S" timestamp) " from " from)))))
(defun jabber-chat-system-prompt (timestamp)
(insert (jabber-propertize
(format-spec jabber-chat-foreign-prompt-format
(list
(cons ?t (format-time-string jabber-chat-time-format
timestamp))
(cons ?n "")
(cons ?u "")
(cons ?r "")
(cons ?j "")))
'face 'jabber-chat-prompt-system
'help-echo
(concat (format-time-string "System message on %Y-%m-%d %H:%M:%S" timestamp)))))
(defun jabber-chat-self-prompt (timestamp delayed dont-print-nick-p)
"Print prompt for sent message.
TIMESTAMP is the timestamp to print, or nil for now.
If DELAYED is true, print long timestamp
\(`jabber-chat-delayed-time-format' as opposed to
`jabber-chat-time-format').
If DONT-PRINT-NICK-P is true, don't include nickname."
(let* ((state-data (fsm-get-state-data jabber-buffer-connection))
(username (plist-get state-data :username))
(server (plist-get state-data :server))
(resource (plist-get state-data :resource))
(nickname username))
(insert (jabber-propertize
(format-spec jabber-chat-local-prompt-format
(list
(cons ?t (format-time-string
(if delayed
jabber-chat-delayed-time-format
jabber-chat-time-format)
timestamp))
(cons ?n (if dont-print-nick-p "" nickname))
(cons ?u username)
(cons ?r resource)
(cons ?j (concat username "@" server))))
'face 'jabber-chat-prompt-local
'help-echo
(concat (format-time-string "On %Y-%m-%d %H:%M:%S" timestamp) " from you")))))
(defun jabber-chat-print-error (xml-data)
"Print error in given <message/> in a readable way."
(let ((the-error (car (jabber-xml-get-children xml-data 'error))))
(insert
(jabber-propertize
(concat "Error: " (jabber-parse-error the-error))
'face 'jabber-chat-error))))
(defun jabber-chat-print-subject (xml-data who mode)
"Print subject of given <message/>, if any."
(let ((subject (car
(jabber-xml-node-children
(car
(jabber-xml-get-children xml-data 'subject))))))
(when (not (zerop (length subject)))
(case mode
(:printp
t)
(:insert
(insert (jabber-propertize
"Subject: " 'face 'jabber-chat-prompt-system)
(jabber-propertize
subject
'face 'jabber-chat-text-foreign)
"\n"))))))
(defun jabber-chat-print-body (xml-data who mode)
(run-hook-with-args-until-success 'jabber-body-printers xml-data who mode))
(defun jabber-chat-normal-body (xml-data who mode)
"Print body for received message in XML-DATA."
(let ((body (car
(jabber-xml-node-children
(car
(jabber-xml-get-children xml-data 'body))))))
(when body
(when (eql mode :insert)
(if (and (> (length body) 4)
(string= (substring body 0 4) "/me "))
(let ((action (substring body 4))
(nick (cond
((eq who :local)
(plist-get (fsm-get-state-data jabber-buffer-connection) :username))
((or (jabber-muc-message-p xml-data)
(jabber-muc-private-message-p xml-data))
(jabber-jid-resource (jabber-xml-get-attribute xml-data 'from)))
(t
(jabber-jid-displayname (jabber-xml-get-attribute xml-data 'from))))))
(insert (jabber-propertize
(concat nick
" "
action)
'face 'jabber-chat-prompt-system)))
(insert (jabber-propertize
body
'face (case who
((:foreign :muc-foreign) 'jabber-chat-text-foreign)
((:local :muc-local) 'jabber-chat-text-local))))))
t)))
(defun jabber-chat-print-url (xml-data who mode)
"Print URLs provided in jabber:x:oob namespace."
(let ((foundp nil))
(dolist (x (jabber-xml-node-children xml-data))
(when (and (listp x) (eq (jabber-xml-node-name x) 'x)
(string= (jabber-xml-get-attribute x 'xmlns) "jabber:x:oob"))
(setq foundp t)
(when (eql mode :insert)
(let ((url (car (jabber-xml-node-children
(car (jabber-xml-get-children x 'url)))))
(desc (car (jabber-xml-node-children
(car (jabber-xml-get-children x 'desc))))))
(insert "\n"
(jabber-propertize
"URL: " 'face 'jabber-chat-prompt-system)
(format "%s <%s>" desc url))))))
foundp))
(defun jabber-chat-goto-address (xml-data who mode)
"Call `goto-address' on the newly written text."
(when (eq mode :insert)
(ignore-errors
;; `goto-address' is autoloaded, but `goto-address-fontify' is not.
(require 'goto-addr)
(let ((end (point))
(limit (max (- (point) 1000) (1+ (point-min)))))
;; We only need to fontify the text written since the last
;; prompt. The prompt has a field property, so we can find it
;; using `field-beginning'.
(goto-address-fontify (field-beginning nil nil limit) end)))))
;; jabber-compose is autoloaded in jabber.el
(add-to-list 'jabber-jid-chat-menu
(cons "Compose message" 'jabber-compose))
(defun jabber-send-message (jc to subject body type)
"send a message tag to the server"
(interactive (list (jabber-read-account)
(jabber-read-jid-completing "to: ")
(jabber-read-with-input-method "subject: ")
(jabber-read-with-input-method "body: ")
(read-string "type: ")))
(jabber-send-sexp jc
`(message ((to . ,to)
,(if (> (length type) 0)
`(type . ,type)))
,(if (> (length subject) 0)
`(subject () ,subject))
,(if (> (length body) 0)
`(body () ,body))))
(if (and jabber-history-enabled (not (string= type "groupchat")))
(jabber-history-log-message "out" nil to body (current-time))))
(add-to-list 'jabber-jid-chat-menu
(cons "Start chat" 'jabber-chat-with))
(defun jabber-chat-with (jc jid &optional other-window)
"Open an empty chat window for chatting with JID.
With a prefix argument, open buffer in other window.
Returns the chat buffer."
(interactive (let* ((jid
(jabber-read-jid-completing "chat with:"))
(account
(jabber-read-account nil jid)))
(list
account jid current-prefix-arg)))
(let ((buffer (jabber-chat-create-buffer jc jid)))
(if other-window
(switch-to-buffer-other-window buffer)
(switch-to-buffer buffer))))
(defun jabber-chat-with-jid-at-point (&optional other-window)
"Start chat with JID at point.
Signal an error if there is no JID at point.
With a prefix argument, open buffer in other window."
(interactive "P")
(let ((jid-at-point (get-text-property (point)
'jabber-jid))
(account (get-text-property (point)
'jabber-account)))
(if (and jid-at-point account)
(jabber-chat-with account jid-at-point other-window)
(error "No contact at point"))))
(provide 'jabber-chat)
;; arch-tag: f423eb92-aa87-475b-b590-48c93ccba9be

View File

@ -1,137 +0,0 @@
;; jabber-chatbuffer.el - functions common to all chat buffers
;; Copyright (C) 2005, 2007, 2008 - Magnus Henoch - mange@freemail.hu
;; This file is a part of jabber.el.
;; 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
(require 'jabber-keymap)
(defvar jabber-point-insert nil
"Position where the message being composed starts")
(defvar jabber-send-function nil
"Function for sending a message from a chat buffer.")
(defvar jabber-chat-mode-hook nil
"Hook called at the end of `jabber-chat-mode'.
Note that functions in this hook have no way of knowing
what kind of chat buffer is being created.")
(defcustom jabber-chat-fill-long-lines t
"If non-nil, fill long lines in chat buffers.
Lines are broken at word boundaries at the width of the
window or at `fill-column', whichever is shorter."
:group 'jabber-chat
:type 'boolean)
(defvar jabber-chat-ewoc nil
"The ewoc showing the messages of this chat buffer.")
;;;###autoload
(defvar jabber-buffer-connection nil
"The connection used by this buffer.")
;;;###autoload
(make-variable-buffer-local 'jabber-buffer-connection)
(defun jabber-chat-mode (jc ewoc-pp)
"\\{jabber-chat-mode-map}"
(kill-all-local-variables)
;; Make sure to set this variable somewhere
(make-local-variable 'jabber-send-function)
(make-local-variable 'scroll-conservatively)
(make-local-variable 'jabber-point-insert)
(make-local-variable 'jabber-chat-ewoc)
(make-local-variable 'buffer-undo-list)
(setq jabber-buffer-connection jc
scroll-conservatively 5
buffer-undo-list t) ;dont keep undo list for chatbuffer
(unless jabber-chat-ewoc
(setq jabber-chat-ewoc
(ewoc-create ewoc-pp nil "---"))
(goto-char (point-max))
(put-text-property (point-min) (point) 'read-only t)
(let ((inhibit-read-only t))
(put-text-property (point-min) (point) 'front-sticky t)
(put-text-property (point-min) (point) 'rear-nonsticky t))
(setq jabber-point-insert (point-marker)))
;;(setq header-line-format jabber-chat-header-line-format)
(setq major-mode 'jabber-chat-mode
mode-name "jabber-chat")
(use-local-map jabber-chat-mode-map)
(if (fboundp 'run-mode-hooks)
(run-mode-hooks 'jabber-chat-mode-hook)
(run-hooks 'jabber-chat-mode-hook)))
(put 'jabber-chat-mode 'mode-class 'special)
;; Spell check only what you're currently writing
(defun jabber-chat-mode-flyspell-verify ()
(>= (point) jabber-point-insert))
(put 'jabber-chat-mode 'flyspell-mode-predicate
'jabber-chat-mode-flyspell-verify)
(defvar jabber-chat-mode-map
(let ((map (make-sparse-keymap)))
(set-keymap-parent map jabber-common-keymap)
(define-key map "\r" 'jabber-chat-buffer-send)
map))
(defun jabber-chat-buffer-send ()
(interactive)
;; If user accidentally hits RET without writing anything, just
;; ignore it.
(when (plusp (- (point-max) jabber-point-insert))
;; If connection was lost...
(unless (memq jabber-buffer-connection jabber-connections)
;; ...maybe there is a new connection to the same account.
(let ((new-jc (jabber-find-active-connection jabber-buffer-connection)))
(if new-jc
;; If so, just use it.
(setq jabber-buffer-connection new-jc)
;; Otherwise, ask for a new account.
(setq jabber-buffer-connection (jabber-read-account t)))))
(let ((body (delete-and-extract-region jabber-point-insert (point-max))))
(funcall jabber-send-function jabber-buffer-connection body))))
(defun jabber-chat-buffer-fill-long-lines ()
"Fill lines that are wider than the window width."
;; This was mostly stolen from article-fill-long-lines
(interactive)
(save-excursion
(let ((inhibit-read-only t)
(width (window-width (get-buffer-window (current-buffer)))))
(goto-char (point-min))
(let ((adaptive-fill-mode nil)) ;Why? -sm
(while (not (eobp))
(end-of-line)
(when (>= (current-column) (min fill-column width))
(save-restriction
(narrow-to-region (min (1+ (point)) (point-max))
(point-at-bol))
(let ((goback (point-marker)))
(fill-paragraph nil)
(goto-char (marker-position goback)))))
(forward-line 1))))))
(provide 'jabber-chatbuffer)
;; arch-tag: 917e5b60-5894-4c49-b3bc-12e1f97ffdc6

View File

@ -1,177 +0,0 @@
;;; jabber-chatstate.el --- Chat state notification (XEP-0085) implementation
;; Author: Ami Fischman <ami@fischman.org>
;; (based entirely on jabber-events.el by Magnus Henoch <mange@freemail.hu>)
;; This file 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.
;; This file 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 GNU Emacs; see the file COPYING. If not, write to
;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
;; Boston, MA 02111-1307, USA.
;; TODO
;; - Currently only active/composing notifications are /sent/ though all 5
;; notifications are handled on receipt.
(require 'cl)
(defgroup jabber-chatstates nil
"Chat state notifications."
:group 'jabber)
(defconst jabber-chatstates-xmlns "http://jabber.org/protocol/chatstates"
"XML namespace for the chatstates feature.")
(defcustom jabber-chatstates-confirm t
"Send notifications about chat states?"
:group 'jabber-chatstates
:type 'boolean)
(defvar jabber-chatstates-requested 'first-time
"Whether or not chat states notification was requested.
This is one of the following:
first-time - send state in first stanza, then switch to nil
t - send states
nil - don't send states")
(make-variable-buffer-local 'jabber-chatstates-requested)
(defvar jabber-chatstates-last-state nil
"The last seen chat state.")
(make-variable-buffer-local 'jabber-chatstates-last-state)
(defvar jabber-chatstates-message ""
"Human-readable presentation of chat state information")
(make-variable-buffer-local 'jabber-chatstates-message)
;;; INCOMING
;;; Code for requesting chat state notifications from others and handling
;;; them.
(defun jabber-chatstates-update-message ()
(setq jabber-chatstates-message
(if (and jabber-chatstates-last-state
(not (eq 'active jabber-chatstates-last-state)))
(format " (%s)" (symbol-name jabber-chatstates-last-state))
"")))
(add-hook 'jabber-chat-send-hooks 'jabber-chatstates-when-sending)
(defun jabber-chatstates-when-sending (text id)
(jabber-chatstates-update-message)
(jabber-chatstates-stop-timer)
(when (and jabber-chatstates-confirm jabber-chatstates-requested)
(when (eq jabber-chatstates-requested 'first-time)
;; don't send more notifications until we know that the other
;; side wants them.
(setq jabber-chatstates-requested nil))
(setq jabber-chatstates-composing-sent nil)
`((active ((xmlns . ,jabber-chatstates-xmlns))))))
;;; OUTGOING
;;; Code for handling requests for chat state notifications and providing
;;; them, modulo user preferences.
(defvar jabber-chatstates-composing-sent nil
"Has composing notification been sent?
It can be sent and cancelled several times.")
(make-variable-buffer-local 'jabber-chatstates-composing-sent)
(defvar jabber-chatstates-paused-timer nil
"Timer that counts down from 'composing state to 'paused.")
(make-variable-buffer-local 'jabber-chatstates-paused-timer)
(defun jabber-chatstates-stop-timer ()
"Stop the 'paused timer."
(when jabber-chatstates-paused-timer
(cancel-timer jabber-chatstates-paused-timer)))
(defun jabber-chatstates-kick-timer ()
"Start (or restart) the 'paused timer as approriate."
(jabber-chatstates-stop-timer)
(setq jabber-chatstates-paused-timer
(run-with-timer 5 nil 'jabber-chatstates-send-paused)))
(defun jabber-chatstates-send-paused ()
"Send an 'paused state notification."
(when (and jabber-chatstates-requested jabber-chatting-with)
(setq jabber-chatstates-composing-sent nil)
(jabber-send-sexp-if-connected
jabber-buffer-connection
`(message
((to . ,jabber-chatting-with)
(type . "chat"))
(paused ((xmlns . ,jabber-chatstates-xmlns)))))))
(defun jabber-chatstates-after-change ()
(let* ((composing-now (not (= (point-max) jabber-point-insert)))
(state (if composing-now 'composing 'active)))
(when (and jabber-chatstates-confirm
jabber-chatting-with
jabber-chatstates-requested
(not (eq composing-now jabber-chatstates-composing-sent)))
(jabber-send-sexp-if-connected
jabber-buffer-connection
`(message
((to . ,jabber-chatting-with)
(type . "chat"))
(,state ((xmlns . ,jabber-chatstates-xmlns)))))
(when (setq jabber-chatstates-composing-sent composing-now)
(jabber-chatstates-kick-timer)))))
;;; COMMON
(defun jabber-handle-incoming-message-chatstates (jc xml-data)
(when (get-buffer (jabber-chat-get-buffer (jabber-xml-get-attribute xml-data 'from)))
(with-current-buffer (jabber-chat-get-buffer (jabber-xml-get-attribute xml-data 'from))
(cond
;; If we get an error message, we shouldn't report any
;; events, as the requests are mirrored from us.
((string= (jabber-xml-get-attribute xml-data 'type) "error")
(remove-hook 'post-command-hook 'jabber-chatstates-after-change t)
(setq jabber-chatstates-requested nil))
(t
(let ((state
(or
(let ((node
(find jabber-chatstates-xmlns
(jabber-xml-node-children xml-data)
:key #'(lambda (x) (jabber-xml-get-attribute x 'xmlns))
:test #'string=)))
(jabber-xml-node-name node))
(let ((node
;; XXX: this is how we interoperate with
;; Google Talk. We should really use a
;; namespace-aware XML parser.
(find jabber-chatstates-xmlns
(jabber-xml-node-children xml-data)
:key #'(lambda (x) (jabber-xml-get-attribute x 'xmlns:cha))
:test #'string=)))
(when node
;; Strip the "cha:" prefix
(let ((name (symbol-name (jabber-xml-node-name node))))
(when (> (length name) 4)
(intern (substring name 4)))))))))
;; Set up hooks for composition notification
(when (and jabber-chatstates-confirm state)
(setq jabber-chatstates-requested t)
(add-hook 'post-command-hook 'jabber-chatstates-after-change nil t))
(setq jabber-chatstates-last-state state)
(jabber-chatstates-update-message)))))))
;; Add function last in chain, so a chat buffer is already created.
(add-to-list 'jabber-message-chain 'jabber-handle-incoming-message-chatstates t)
(jabber-disco-advertise-feature "http://jabber.org/protocol/chatstates")
(provide 'jabber-chatstates)
;; arch-tag: d879de90-51e1-11dc-909d-000a95c2fcd0

View File

@ -1,82 +0,0 @@
;;; jabber-compose.el --- compose a Jabber message in a buffer
;; Copyright (C) 2006, 2007 Magnus Henoch
;; Author: Magnus Henoch <mange@freemail.hu>
;; Keywords:
;; This file 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.
;; This file 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 GNU Emacs; see the file COPYING. If not, write to
;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
;; Boston, MA 02110-1301, USA.
;;; Code:
;;;###autoload
(defun jabber-compose (jc &optional recipient)
"Create a buffer for composing a Jabber message."
(interactive (list (jabber-read-account)
(jabber-read-jid-completing "To whom? ")))
(with-current-buffer (get-buffer-create
(generate-new-buffer-name
(concat
"Jabber-Compose"
(when recipient
(format "-%s" (jabber-jid-displayname recipient))))))
(set (make-local-variable 'jabber-widget-alist) nil)
(setq jabber-buffer-connection jc)
(use-local-map widget-keymap)
(insert (jabber-propertize "Compose Jabber message\n" 'face 'jabber-title-large))
(insert (substitute-command-keys "\\<widget-field-keymap>Completion available with \\[widget-complete].\n"))
(push (cons :recipients
(widget-create '(repeat :tag "Recipients" jid)
:value (when recipient
(list recipient))))
jabber-widget-alist)
(insert "\nSubject: ")
(push (cons :subject
(widget-create 'editable-field :value ""))
jabber-widget-alist)
(insert "\nText:\n")
(push (cons :text
(widget-create 'text :value ""))
jabber-widget-alist)
(insert "\n")
(widget-create 'push-button :notify #'jabber-compose-send "Send")
(widget-setup)
(switch-to-buffer (current-buffer))
(goto-char (point-min))))
(defun jabber-compose-send (&rest ignore)
(let ((recipients (widget-value (cdr (assq :recipients jabber-widget-alist))))
(subject (widget-value (cdr (assq :subject jabber-widget-alist))))
(text (widget-value (cdr (assq :text jabber-widget-alist)))))
(when (null recipients)
(error "No recipients specified"))
(dolist (to recipients)
(jabber-send-message jabber-buffer-connection to subject text nil))
(bury-buffer)
(message "Message sent")))
(provide 'jabber-compose)
;; arch-tag: 59032c00-994d-11da-8d97-000a95c2fcd0

View File

@ -1,405 +0,0 @@
;; jabber-conn.el - Network transport functions
;; Copyright (C) 2005 - Georg Lehner - jorge@magma.com.ni
;; mostly inspired by Gnus.
;; Copyright (C) 2005 - Carl Henrik Lunde - chlunde+jabber+@ping.uio.no
;; (starttls)
;; This file is a part of jabber.el.
;; 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
;; A collection of functions, that hide the details of transmitting to
;; and fro a Jabber Server
(eval-when-compile (require 'cl))
;; Emacs 24 can be linked with GnuTLS
(ignore-errors (require 'gnutls))
;; Try two different TLS/SSL libraries, but don't fail if none available.
(or (ignore-errors (require 'tls))
(ignore-errors (require 'ssl)))
(ignore-errors (require 'starttls))
(eval-and-compile
(or (ignore-errors (require 'srv))
(ignore-errors
(let ((load-path (cons (expand-file-name
"jabber-fallback-lib"
(file-name-directory (locate-library "jabber")))
load-path)))
(require 'srv)))
(error
"srv not found in `load-path' or jabber-fallback-lib/ directory.")))
(defgroup jabber-conn nil "Jabber Connection Settings"
:group 'jabber)
(defun jabber-have-starttls ()
"Return true if we can use STARTTLS."
(or (and (fboundp 'gnutls-available-p)
(gnutls-available-p))
(and (featurep 'starttls)
(or (and (bound-and-true-p starttls-gnutls-program)
(executable-find starttls-gnutls-program))
(and (bound-and-true-p starttls-program)
(executable-find starttls-program))))))
(defconst jabber-default-connection-type
(cond
;; Use STARTTLS if we can...
((jabber-have-starttls)
'starttls)
;; ...else default to unencrypted connection.
(t
'network))
"Default connection type.
See `jabber-connect-methods'.")
(defcustom jabber-connection-ssl-program nil
"Program used for SSL/TLS connections.
nil means prefer gnutls but fall back to openssl.
'gnutls' means use gnutls (through `open-tls-stream').
'openssl means use openssl (through `open-ssl-stream')."
:type '(choice (const :tag "Prefer gnutls, fall back to openssl" nil)
(const :tag "Use gnutls" gnutls)
(const :tag "Use openssl" openssl))
:group 'jabber-conn)
(defcustom jabber-invalid-certificate-servers ()
"Jabber servers for which we accept invalid TLS certificates.
This is a list of server names, each matching the hostname part
of your JID.
This option has effect only when using native GnuTLS in Emacs 24
or later."
:type '(repeat string)
:group 'jabber-conn)
(defvar jabber-connect-methods
`((network jabber-network-connect jabber-network-send)
(starttls
,(if (and (fboundp 'gnutls-available-p)
(gnutls-available-p))
;; With "native" TLS, we can use a normal connection.
'jabber-network-connect
'jabber-starttls-connect)
jabber-network-send)
(ssl jabber-ssl-connect jabber-ssl-send)
(virtual jabber-virtual-connect jabber-virtual-send))
"Alist of connection methods and functions.
First item is the symbol naming the method.
Second item is the connect function.
Third item is the send function.")
(defun jabber-get-connect-function (type)
"Get the connect function associated with TYPE.
TYPE is a symbol; see `jabber-connection-type'."
(let ((entry (assq type jabber-connect-methods)))
(nth 1 entry)))
(defun jabber-get-send-function (type)
"Get the send function associated with TYPE.
TYPE is a symbol; see `jabber-connection-type'."
(let ((entry (assq type jabber-connect-methods)))
(nth 2 entry)))
(defun jabber-srv-targets (server network-server port)
"Find host and port to connect to.
If NETWORK-SERVER and/or PORT are specified, use them.
If we can't find SRV records, use standard defaults."
;; If the user has specified a host or a port, obey that.
(if (or network-server port)
(list (cons (or network-server server)
(or port 5222)))
(or (condition-case nil
(srv-lookup (concat "_xmpp-client._tcp." server))
(error nil))
(list (cons server 5222)))))
;; Plain TCP/IP connection
(defun jabber-network-connect (fsm server network-server port)
"Connect to a Jabber server with a plain network connection.
Send a message of the form (:connected CONNECTION) to FSM if
connection succeeds. Send a message (:connection-failed ERRORS) if
connection fails."
(cond
((featurep 'make-network-process '(:nowait t))
;; We can connect asynchronously!
(jabber-network-connect-async fsm server network-server port))
(t
;; Connecting to the server will block Emacs.
(jabber-network-connect-sync fsm server network-server port))))
(defun jabber-network-connect-async (fsm server network-server port)
;; Get all potential targets...
(lexical-let ((targets (jabber-srv-targets server network-server port))
errors
(fsm fsm))
;; ...and connect to them one after another, asynchronously, until
;; connection succeeds.
(labels
((connect
(target remaining-targets)
(lexical-let ((target target) (remaining-targets remaining-targets))
(labels ((connection-successful
(c)
;; This mustn't be `fsm-send-sync', because the FSM
;; needs to change the sentinel, which cannot be done
;; from inside the sentinel.
(fsm-send fsm (list :connected c)))
(connection-failed
(c status)
(when (and (> (length status) 0)
(eq (aref status (1- (length status))) ?\n))
(setq status (substring status 0 -1)))
(let ((err
(format "Couldn't connect to %s:%s: %s"
(car target) (cdr target) status)))
(message "%s" err)
(push err errors))
(when c (delete-process c))
(if remaining-targets
(progn
(message
"Connecting to %s:%s..."
(caar remaining-targets) (cdar remaining-targets))
(connect (car remaining-targets) (cdr remaining-targets)))
(fsm-send fsm (list :connection-failed (nreverse errors))))))
(condition-case e
(make-network-process
:name "jabber"
:buffer (generate-new-buffer jabber-process-buffer)
:host (car target) :service (cdr target)
:coding 'utf-8
:nowait t
:sentinel
(lexical-let ((target target) (remaining-targets remaining-targets))
(lambda (connection status)
(cond
((string-match "^open" status)
(connection-successful connection))
((string-match "^failed" status)
(connection-failed connection status))
((string-match "^deleted" status)
;; This happens when we delete a process in the
;; "failed" case above.
nil)
(t
(message "Unknown sentinel status `%s'" status))))))
(file-error
;; A file-error has the error message in the third list
;; element.
(connection-failed nil (car (cddr e))))
(error
;; Not sure if we ever get anything but file-errors,
;; but let's make sure we report them:
(connection-failed nil (error-message-string e))))))))
(message "Connecting to %s:%s..." (caar targets) (cdar targets))
(connect (car targets) (cdr targets)))))
(defun jabber-network-connect-sync (fsm server network-server port)
;; This code will AFAIK only be used on Windows. Apologies in
;; advance for any bit rot...
(let ((coding-system-for-read 'utf-8)
(coding-system-for-write 'utf-8)
(targets (jabber-srv-targets server network-server port))
errors)
(catch 'connected
(dolist (target targets)
(condition-case e
(let ((process-buffer (generate-new-buffer jabber-process-buffer))
connection)
(unwind-protect
(setq connection (open-network-stream
"jabber"
process-buffer
(car target)
(cdr target)))
(unless (or connection jabber-debug-keep-process-buffers)
(kill-buffer process-buffer)))
(when connection
(fsm-send fsm (list :connected connection))
(throw 'connected connection)))
(file-error
;; A file-error has the error message in the third list
;; element.
(let ((err (format "Couldn't connect to %s:%s: %s"
(car target) (cdr target)
(car (cddr e)))))
(message "%s" err)
(push err errors)))
(error
;; Not sure if we ever get anything but file-errors,
;; but let's make sure we report them:
(let ((err (format "Couldn't connect to %s:%s: %s"
(car target) (cdr target)
(error-message-string e))))
(message "%s" err)
(push err errors)))))
(fsm-send fsm (list :connection-failed (nreverse errors))))))
(defun jabber-network-send (connection string)
"Send a string via a plain TCP/IP connection to the Jabber Server."
(process-send-string connection string))
;; SSL connection, we use openssl's s_client function for encryption
;; of the link
;; TODO: make this configurable
(defun jabber-ssl-connect (fsm server network-server port)
"connect via OpenSSL or GnuTLS to a Jabber Server
Send a message of the form (:connected CONNECTION) to FSM if
connection succeeds. Send a message (:connection-failed ERRORS) if
connection fails."
(let ((coding-system-for-read 'utf-8)
(coding-system-for-write 'utf-8)
(connect-function
(cond
((and (memq jabber-connection-ssl-program '(nil gnutls))
(fboundp 'open-tls-stream))
'open-tls-stream)
((and (memq jabber-connection-ssl-program '(nil openssl))
(fboundp 'open-ssl-stream))
'open-ssl-stream)
(t
(error "Neither TLS nor SSL connect functions available"))))
error-msg)
(let ((process-buffer (generate-new-buffer jabber-process-buffer))
connection)
(setq network-server (or network-server server))
(setq port (or port 5223))
(condition-case e
(setq connection (funcall connect-function
"jabber"
process-buffer
network-server
port))
(error
(setq error-msg
(format "Couldn't connect to %s:%d: %s" network-server port
(error-message-string e)))
(message "%s" error-msg)))
(unless (or connection jabber-debug-keep-process-buffers)
(kill-buffer process-buffer))
(if connection
(fsm-send fsm (list :connected connection))
(fsm-send fsm (list :connection-failed
(when error-msg (list error-msg))))))))
(defun jabber-ssl-send (connection string)
"Send a string via an SSL-encrypted connection to the Jabber Server."
;; It seems we need to send a linefeed afterwards.
(process-send-string connection string)
(process-send-string connection "\n"))
(defun jabber-starttls-connect (fsm server network-server port)
"Connect via an external GnuTLS process to a Jabber Server.
Send a message of the form (:connected CONNECTION) to FSM if
connection succeeds. Send a message (:connection-failed ERRORS) if
connection fails."
(let ((coding-system-for-read 'utf-8)
(coding-system-for-write 'utf-8)
(targets (jabber-srv-targets server network-server port))
errors)
(unless (fboundp 'starttls-open-stream)
(error "starttls.el not available"))
(catch 'connected
(dolist (target targets)
(condition-case e
(let ((process-buffer (generate-new-buffer jabber-process-buffer))
connection)
(unwind-protect
(setq connection
(starttls-open-stream
"jabber"
process-buffer
(car target)
(cdr target)))
(unless (or connection jabber-debug-keep-process-buffers)
(kill-buffer process-buffer)))
(if (null connection)
;; It seems we don't actually get an error if we
;; can't connect. Let's try to convey some useful
;; information to the user at least.
(let ((err (format "Couldn't connect to %s:%s"
(car target) (cdr target))))
(message "%s" err)
(push err errors))
(fsm-send fsm (list :connected connection))
(throw 'connected connection)))
(error
(let ((err (format "Couldn't connect to %s: %s" target
(error-message-string e))))
(message "%s" err)
(push err errors)))))
(fsm-send fsm (list :connection-failed (nreverse errors))))))
(defun jabber-starttls-initiate (fsm)
"Initiate a starttls connection"
(jabber-send-sexp fsm
'(starttls ((xmlns . "urn:ietf:params:xml:ns:xmpp-tls")))))
(defun jabber-starttls-process-input (fsm xml-data)
"Process result of starttls request.
On failure, signal error."
(cond
((eq (car xml-data) 'proceed)
(let* ((state-data (fsm-get-state-data fsm))
(connection (plist-get state-data :connection)))
;; Did we use open-network-stream or starttls-open-stream? We
;; can tell by process-type.
(case (process-type connection)
(network
(let* ((hostname (plist-get state-data :server))
(verifyp (not (member hostname jabber-invalid-certificate-servers))))
;; gnutls-negotiate might signal an error, which is caught
;; by our caller
(gnutls-negotiate
:process connection
;; This is the hostname that the certificate should be valid for:
:hostname hostname
:verify-hostname-error verifyp
:verify-error verifyp)))
(real
(or
(starttls-negotiate connection)
(error "Negotiation failure"))))))
((eq (car xml-data) 'failure)
(error "Command rejected by server"))))
(defvar *jabber-virtual-server-function* nil
"Function to use for sending stanzas on a virtual connection.
The function should accept two arguments, the connection object
and a string that the connection wants to send.")
(defun jabber-virtual-connect (fsm server network-server port)
"Connect to a virtual \"server\".
Use `*jabber-virtual-server-function*' as send function."
(unless (functionp *jabber-virtual-server-function*)
(error "No virtual server function specified"))
;; We pass the fsm itself as "connection object", as that is what a
;; virtual server needs to send stanzas.
(fsm-send fsm (list :connected fsm)))
(defun jabber-virtual-send (connection string)
(funcall *jabber-virtual-server-function* connection string))
(provide 'jabber-conn)
;; arch-tag: f95ec240-8cd3-11d9-9dbf-000a95c2fcd0

View File

@ -1,143 +0,0 @@
;; jabber-console.el - XML Console mode
;; Copyright (C) 2009, 2010 - Demyan Rogozhin <demyan.rogozhin@gmail.com>
;; 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
;;; Commentary:
;; Use *-jabber-console-* for sending custom XMPP code. Be careful!
;;; Code:
(require 'jabber-keymap)
(require 'jabber-util)
(require 'ewoc)
(require 'sgml-mode) ;we base on this mode to hightlight XML
(defcustom jabber-console-name-format "*-jabber-console-%s-*"
"Format for console buffer name. %s mean connection jid."
:type 'string
:group 'jabber-debug)
(defcustom jabber-console-truncate-lines 3000
"Maximum number of lines in console buffer.
Not truncate if set to 0"
:type 'integer
:group 'jabber-debug)
(defvar jabber-point-insert nil
"Position where the message being composed starts")
(defvar jabber-send-function nil
"Function for sending a message from a chat buffer.")
(defvar jabber-console-mode-hook nil
"Hook called at the end of `jabber-console-mode'.
Note that functions in this hook have no way of knowing
what kind of chat buffer is being created.")
(defvar jabber-console-ewoc nil
"The ewoc showing the XML elements of this stream buffer.")
(defvar jabber-console-mode-map
(let ((map (make-sparse-keymap)))
(set-keymap-parent map jabber-common-keymap)
(define-key map "\r" 'jabber-chat-buffer-send)
map))
(defun jabber-console-create-buffer (jc)
(with-current-buffer
(get-buffer-create (format jabber-console-name-format (jabber-connection-bare-jid jc)))
(unless (eq major-mode 'jabber-console-mode)
(jabber-console-mode))
;; Make sure the connection variable is up to date.
(setq jabber-buffer-connection jc)
(current-buffer)))
(defun jabber-console-send (jc data)
;; Put manual string into buffers ewoc
(jabber-process-console jc "raw" data)
;; ...than sent it to server
(jabber-send-string jc data))
(defun jabber-console-comment (str)
"Insert comment into console buffer."
(let ((string (concat
comment-start str "@" (jabber-encode-time (current-time)) ":"
comment-end "\n")))
(when (stringp jabber-debug-log-xml)
(jabber-append-string-to-file string jabber-debug-log-xml))
(insert string)))
(defun jabber-console-pp (data)
"Pretty Printer for XML-sexp and raw data"
(let ((direction (car data))
(xml-list (cdr data))
(raw (cadr data)))
(jabber-console-comment direction)
(if (stringp raw)
;; raw code input
(progn
(insert raw)
(when (stringp jabber-debug-log-xml)
(jabber-append-string-to-file raw jabber-debug-log-xml)))
;; receive/sending
(progn
(xml-print xml-list)
(when (stringp jabber-debug-log-xml)
(jabber-append-string-to-file
"\n" jabber-debug-log-xml 'xml-print xml-list))))))
(define-derived-mode jabber-console-mode sgml-mode "Jabber Console"
"Major mode for debug XMPP protocol"
;; Make sure to set this variable somewhere
(make-local-variable 'jabber-send-function)
(make-local-variable 'jabber-point-insert)
(make-local-variable 'jabber-console-ewoc)
(setq jabber-send-function 'jabber-console-send)
(unless jabber-console-ewoc
(setq jabber-console-ewoc
(ewoc-create #'jabber-console-pp nil "<!-- + -->"))
(goto-char (point-max))
(put-text-property (point-min) (point) 'read-only t)
(let ((inhibit-read-only t))
(put-text-property (point-min) (point) 'front-sticky t)
(put-text-property (point-min) (point) 'rear-nonsticky t))
(setq jabber-point-insert (point-marker))))
(put 'jabber-console-mode 'mode-class 'special)
(defun jabber-console-sanitize (xml-data)
"Sanitize XML-DATA for jabber-process-console"
(if (listp xml-data)
(jabber-tree-map (lambda (x) (if (numberp x) (format "%s" x) x)) xml-data)
xml-data))
;;;###autoload
(defun jabber-process-console (jc direction xml-data)
"Log XML-DATA i/o as XML in \"*-jabber-console-JID-*\" buffer"
(let ((buffer (get-buffer-create (jabber-console-create-buffer jc))))
(with-current-buffer buffer
(progn
(ewoc-enter-last jabber-console-ewoc (list direction (jabber-console-sanitize xml-data)))
(when (< 1 jabber-console-truncate-lines)
(let ((jabber-log-lines-to-keep jabber-console-truncate-lines))
(jabber-truncate-top buffer jabber-console-ewoc)))))))
(provide 'jabber-console)
;;; jabber-console.el ends here

File diff suppressed because it is too large Load Diff

View File

@ -1,652 +0,0 @@
;; jabber-disco.el - service discovery functions
;; Copyright (C) 2003, 2004, 2007, 2008 - Magnus Henoch - mange@freemail.hu
;; Copyright (C) 2002, 2003, 2004 - tom berger - object@intelectronica.net
;; This file is a part of jabber.el.
;; 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
(require 'jabber-iq)
(require 'jabber-xml)
(require 'jabber-menu)
;;; Respond to disco requests
(defvar jabber-advertised-features
(list "http://jabber.org/protocol/disco#info")
"Features advertised on service discovery requests
Don't add your feature to this list directly. Instead, call
`jabber-disco-advertise-feature'.")
(defvar jabber-disco-items-nodes
(list
(list "" nil nil))
"Alist of node names and information about returning disco item data.
Key is node name as a string, or \"\" for no node specified. Value is
a list of two items.
First item is data to return. If it is a function, that function is
called and its return value is used; if it is a list, that list is
used. The list should be the XML data to be returned inside the
<query/> element, like this:
\((item ((name . \"Name of first item\")
(jid . \"first.item\")
(node . \"node\"))))
Second item is access control function. That function is passed the
JID, and returns non-nil if access is granted. If the second item is
nil, access is always granted.")
(defvar jabber-disco-info-nodes
(list
(list "" #'jabber-disco-return-client-info nil))
"Alist of node names and information returning disco info data.
Key is node name as a string, or \"\" for no node specified. Value is
a list of two items.
First item is data to return. If it is a function, that function is
called and its return value is used; if it is a list, that list is
used. The list should be the XML data to be returned inside the
<query/> element, like this:
\((identity ((category . \"client\")
(type . \"pc\")
(name . \"Jabber client\")))
(feature ((var . \"some-feature\"))))
Second item is access control function. That function is passed the
JID, and returns non-nil if access is granted. If the second item is
nil, access is always granted.")
(add-to-list 'jabber-iq-get-xmlns-alist
(cons "http://jabber.org/protocol/disco#info" 'jabber-return-disco-info))
(add-to-list 'jabber-iq-get-xmlns-alist
(cons "http://jabber.org/protocol/disco#items" 'jabber-return-disco-info))
(defun jabber-return-disco-info (jc xml-data)
"Respond to a service discovery request.
See JEP-0030."
(let* ((to (jabber-xml-get-attribute xml-data 'from))
(id (jabber-xml-get-attribute xml-data 'id))
(xmlns (jabber-iq-xmlns xml-data))
(which-alist (eval (cdr (assoc xmlns
(list
(cons "http://jabber.org/protocol/disco#info" 'jabber-disco-info-nodes)
(cons "http://jabber.org/protocol/disco#items" 'jabber-disco-items-nodes))))))
(node (or
(jabber-xml-get-attribute (jabber-iq-query xml-data) 'node)
""))
(return-list (cdr (assoc node which-alist)))
(func (nth 0 return-list))
(access-control (nth 1 return-list)))
(if return-list
(if (and (functionp access-control)
(not (funcall access-control jc to)))
(jabber-signal-error "cancel" 'not-allowed)
;; Access control passed
(let ((result (if (functionp func)
(funcall func jc xml-data)
func)))
(jabber-send-iq jc to "result"
`(query ((xmlns . ,xmlns)
,@(when node
(list (cons 'node node))))
,@result)
nil nil nil nil id)))
;; No such node
(jabber-signal-error "cancel" 'item-not-found))))
(defun jabber-disco-return-client-info (&optional jc xml-data)
`(
;; If running under a window system, this is
;; a GUI client. If not, it is a console client.
(identity ((category . "client")
(name . "Emacs Jabber client")
(type . ,(if (memq window-system
'(x w32 mac ns))
"pc"
"console"))))
,@(mapcar
#'(lambda (featurename)
`(feature ((var . ,featurename))))
jabber-advertised-features)))
;;; Interactive disco requests
(add-to-list 'jabber-jid-info-menu
(cons "Send items disco query" 'jabber-get-disco-items))
(defun jabber-get-disco-items (jc to &optional node)
"Send a service discovery request for items"
(interactive (list (jabber-read-account)
(jabber-read-jid-completing "Send items disco request to: " nil nil nil 'full t)
(jabber-read-node "Node (or leave empty): ")))
(jabber-send-iq jc to
"get"
(list 'query (append (list (cons 'xmlns "http://jabber.org/protocol/disco#items"))
(if (> (length node) 0)
(list (cons 'node node)))))
#'jabber-process-data #'jabber-process-disco-items
#'jabber-process-data "Item discovery failed"))
(add-to-list 'jabber-jid-info-menu
(cons "Send info disco query" 'jabber-get-disco-info))
(defun jabber-get-disco-info (jc to &optional node)
"Send a service discovery request for info"
(interactive (list (jabber-read-account)
(jabber-read-jid-completing "Send info disco request to: " nil nil nil 'full t)
(jabber-read-node "Node (or leave empty): ")))
(jabber-send-iq jc to
"get"
(list 'query (append (list (cons 'xmlns "http://jabber.org/protocol/disco#info"))
(if (> (length node) 0)
(list (cons 'node node)))))
#'jabber-process-data #'jabber-process-disco-info
#'jabber-process-data "Info discovery failed"))
(defun jabber-process-disco-info (jc xml-data)
"Handle results from info disco requests."
(let ((beginning (point)))
(dolist (x (jabber-xml-node-children (jabber-iq-query xml-data)))
(cond
((eq (jabber-xml-node-name x) 'identity)
(let ((name (jabber-xml-get-attribute x 'name))
(category (jabber-xml-get-attribute x 'category))
(type (jabber-xml-get-attribute x 'type)))
(insert (jabber-propertize (if name
name
"Unnamed")
'face 'jabber-title-medium)
"\n\nCategory:\t" category "\n")
(if type
(insert "Type:\t\t" type "\n"))
(insert "\n")))
((eq (jabber-xml-node-name x) 'feature)
(let ((var (jabber-xml-get-attribute x 'var)))
(insert "Feature:\t" var "\n")))))
(put-text-property beginning (point)
'jabber-jid (jabber-xml-get-attribute xml-data 'from))
(put-text-property beginning (point)
'jabber-account jc)))
(defun jabber-process-disco-items (jc xml-data)
"Handle results from items disco requests."
(let ((items (jabber-xml-get-children (jabber-iq-query xml-data) 'item)))
(if items
(dolist (item items)
(let ((jid (jabber-xml-get-attribute item 'jid))
(name (jabber-xml-get-attribute item 'name))
(node (jabber-xml-get-attribute item 'node)))
(insert
(jabber-propertize
(concat
(jabber-propertize
(concat jid "\n" (if node (format "Node: %s\n" node)))
'face 'jabber-title-medium)
name "\n\n")
'jabber-jid jid
'jabber-account jc
'jabber-node node))))
(insert "No items found.\n"))))
;;; Caching API for disco requests
;; Keys are ("jid" . "node"), where "node" is nil if appropriate.
;; Values are (identities features), where each identity is ["name"
;; "category" "type"], and each feature is a string.
(defvar jabber-disco-info-cache (make-hash-table :test 'equal))
;; Keys are ("jid" . "node"). Values are (items), where each
;; item is ["name" "jid" "node"] (some values may be nil).
(defvar jabber-disco-items-cache (make-hash-table :test 'equal))
(defun jabber-disco-get-info (jc jid node callback closure-data &optional force)
"Get disco info for JID and NODE, using connection JC.
Call CALLBACK with JC and CLOSURE-DATA as first and second
arguments and result as third argument when result is available.
On success, result is (IDENTITIES FEATURES), where each identity is [\"name\"
\"category\" \"type\"], and each feature is a string.
On error, result is the error node, recognizable by (eq (car result) 'error).
If CALLBACK is nil, just fetch data. If FORCE is non-nil,
invalidate cache and get fresh data."
(when force
(remhash (cons jid node) jabber-disco-info-cache))
(let ((result (unless force (jabber-disco-get-info-immediately jid node))))
(if result
(and callback (run-with-timer 0 nil callback jc closure-data result))
(jabber-send-iq jc jid
"get"
`(query ((xmlns . "http://jabber.org/protocol/disco#info")
,@(when node `((node . ,node)))))
#'jabber-disco-got-info (cons callback closure-data)
(lambda (jc xml-data callback-data)
(when (car callback-data)
(funcall (car callback-data) jc (cdr callback-data) (jabber-iq-error xml-data))))
(cons callback closure-data)))))
(defun jabber-disco-got-info (jc xml-data callback-data)
(let ((jid (jabber-xml-get-attribute xml-data 'from))
(node (jabber-xml-get-attribute (jabber-iq-query xml-data)
'node))
(result (jabber-disco-parse-info xml-data)))
(puthash (cons jid node) result jabber-disco-info-cache)
(when (car callback-data)
(funcall (car callback-data) jc (cdr callback-data) result))))
(defun jabber-disco-parse-info (xml-data)
"Extract data from an <iq/> stanza containing a disco#info result.
See `jabber-disco-get-info' for a description of the return value."
(list
(mapcar
#'(lambda (id)
(vector (jabber-xml-get-attribute id 'name)
(jabber-xml-get-attribute id 'category)
(jabber-xml-get-attribute id 'type)))
(jabber-xml-get-children (jabber-iq-query xml-data) 'identity))
(mapcar
#'(lambda (feature)
(jabber-xml-get-attribute feature 'var))
(jabber-xml-get-children (jabber-iq-query xml-data) 'feature))))
(defun jabber-disco-get-info-immediately (jid node)
"Get cached disco info for JID and NODE.
Return nil if no info available.
Fill the cache with `jabber-disco-get-info'."
(or
;; Check "normal" cache...
(gethash (cons jid node) jabber-disco-info-cache)
;; And then check Entity Capabilities.
(and (null node) (jabber-caps-get-cached jid))))
(defun jabber-disco-get-items (jc jid node callback closure-data &optional force)
"Get disco items for JID and NODE, using connection JC.
Call CALLBACK with JC and CLOSURE-DATA as first and second
arguments and items result as third argument when result is
available.
On success, result is a list of items, where each
item is [\"name\" \"jid\" \"node\"] (some values may be nil).
On error, result is the error node, recognizable by (eq (car result) 'error).
If CALLBACK is nil, just fetch data. If FORCE is non-nil,
invalidate cache and get fresh data."
(when force
(remhash (cons jid node) jabber-disco-items-cache))
(let ((result (gethash (cons jid node) jabber-disco-items-cache)))
(if result
(and callback (run-with-timer 0 nil callback jc closure-data result))
(jabber-send-iq jc jid
"get"
`(query ((xmlns . "http://jabber.org/protocol/disco#items")
,@(when node `((node . ,node)))))
#'jabber-disco-got-items (cons callback closure-data)
(lambda (jc xml-data callback-data)
(when (car callback-data)
(funcall (car callback-data) jc (cdr callback-data) (jabber-iq-error xml-data))))
(cons callback closure-data)))))
(defun jabber-disco-got-items (jc xml-data callback-data)
(let ((jid (jabber-xml-get-attribute xml-data 'from))
(node (jabber-xml-get-attribute (jabber-iq-query xml-data)
'node))
(result
(mapcar
#'(lambda (item)
(vector
(jabber-xml-get-attribute item 'name)
(jabber-xml-get-attribute item 'jid)
(jabber-xml-get-attribute item 'node)))
(jabber-xml-get-children (jabber-iq-query xml-data) 'item))))
(puthash (cons jid node) result jabber-disco-items-cache)
(when (car callback-data)
(funcall (car callback-data) jc (cdr callback-data) result))))
(defun jabber-disco-get-items-immediately (jid node)
(gethash (cons jid node) jabber-disco-items-cache))
;;; Publish
(defun jabber-disco-publish (jc node item-name item-jid item-node)
"Publish the given item under disco node NODE."
(jabber-send-iq jc nil
"set"
`(query ((xmlns . "http://jabber.org/protocol/disco#items")
,@(when node `((node . ,node))))
(item ((action . "update")
(jid . ,item-jid)
,@(when item-name
`((name . ,item-name)))
,@(when item-node
`((node . ,item-node))))))
'jabber-report-success "Disco publish"
'jabber-report-success "Disco publish"))
(defun jabber-disco-publish-remove (jc node item-jid item-node)
"Remove the given item from published disco items."
(jabber-send-iq jc nil
"set"
`(query ((xmlns . "http://jabber.org/protocol/disco#items")
,@(when node `((node . ,node))))
(item ((action . "remove")
(jid . ,item-jid)
,@(when item-node
`((node . ,item-node))))))
'jabber-report-success "Disco removal"
'jabber-report-success "Disco removal"))
;;; Entity Capabilities (XEP-0115)
;;;###autoload
(eval-after-load "jabber-core"
'(add-to-list 'jabber-presence-chain #'jabber-process-caps))
(defvar jabber-caps-cache (make-hash-table :test 'equal))
(defconst jabber-caps-hash-names
(if (fboundp 'secure-hash)
'(("sha-1" . sha1)
("sha-224" . sha224)
("sha-256" . sha256)
("sha-384" . sha384)
("sha-512" . sha512))
;; `secure-hash' was introduced in Emacs 24. For Emacs 23, fall
;; back to the `sha1' function, handled specially in
;; `jabber-caps--secure-hash'.
'(("sha-1" . sha1)))
"Hash function name map.
Maps names defined in http://www.iana.org/assignments/hash-function-text-names
to symbols accepted by `secure-hash'.
XEP-0115 currently recommends SHA-1, but let's be future-proof.")
(defun jabber-caps-get-cached (jid)
"Get disco info from Entity Capabilities cache.
JID should be a string containing a full JID.
Return (IDENTITIES FEATURES), or nil if not in cache."
(let* ((symbol (jabber-jid-symbol jid))
(resource (or (jabber-jid-resource jid) ""))
(resource-plist (cdr (assoc resource (get symbol 'resources))))
(key (plist-get resource-plist 'caps)))
(when key
(let ((cache-entry (gethash key jabber-caps-cache)))
(when (and (consp cache-entry) (not (floatp (car cache-entry))))
cache-entry)))))
;;;###autoload
(defun jabber-process-caps (jc xml-data)
"Look for entity capabilities in presence stanzas."
(let* ((from (jabber-xml-get-attribute xml-data 'from))
(type (jabber-xml-get-attribute xml-data 'type))
(c (jabber-xml-path xml-data '(("http://jabber.org/protocol/caps" . "c")))))
(when (and (null type) c)
(jabber-xml-let-attributes
(ext hash node ver) c
(cond
(hash
;; If the <c/> element has a hash attribute, it follows the
;; "modern" version of XEP-0115.
(jabber-process-caps-modern jc from hash node ver))
(t
;; No hash attribute. Use legacy version of XEP-0115.
;; TODO: do something clever here.
))))))
(defun jabber-process-caps-modern (jc jid hash node ver)
(when (assoc hash jabber-caps-hash-names)
;; We support the hash function used.
(let* ((key (cons hash ver))
(cache-entry (gethash key jabber-caps-cache)))
;; Remember the hash in the JID symbol.
(let* ((symbol (jabber-jid-symbol jid))
(resource (or (jabber-jid-resource jid) ""))
(resource-entry (assoc resource (get symbol 'resources)))
(new-resource-plist (plist-put (cdr resource-entry) 'caps key)))
(if resource-entry
(setf (cdr resource-entry) new-resource-plist)
(push (cons resource new-resource-plist) (get symbol 'resources))))
(flet ((request-disco-info
()
(jabber-send-iq
jc jid
"get"
`(query ((xmlns . "http://jabber.org/protocol/disco#info")
(node . ,(concat node "#" ver))))
#'jabber-process-caps-info-result (list hash node ver)
#'jabber-process-caps-info-error (list hash node ver))))
(cond
((and (consp cache-entry)
(floatp (car cache-entry)))
;; We have a record of asking someone about this hash.
(if (< (- (float-time) (car cache-entry)) 10.0)
;; We asked someone about this hash less than 10 seconds ago.
;; Let's add the new JID to the entry, just in case that
;; doesn't work out.
(pushnew jid (cdr cache-entry) :test #'string=)
;; We asked someone about it more than 10 seconds ago.
;; They're probably not going to answer. Let's ask
;; this contact about it instead.
(setf (car cache-entry) (float-time))
(request-disco-info)))
((null cache-entry)
;; We know nothing about this hash. Let's note the
;; fact that we tried to get information about it.
(puthash key (list (float-time)) jabber-caps-cache)
(request-disco-info))
(t
;; We already know what this hash represents, so we
;; can cache info for this contact.
(puthash (cons jid nil) cache-entry jabber-disco-info-cache)))))))
(defun jabber-process-caps-info-result (jc xml-data closure-data)
(destructuring-bind (hash node ver) closure-data
(let* ((key (cons hash ver))
(query (jabber-iq-query xml-data))
(verification-string (jabber-caps-ver-string query hash)))
(if (string= ver verification-string)
;; The hash is correct; save info.
(puthash key (jabber-disco-parse-info xml-data) jabber-caps-cache)
;; The hash is incorrect.
(jabber-caps-try-next jc hash node ver)))))
(defun jabber-process-caps-info-error (jc xml-data closure-data)
(destructuring-bind (hash node ver) closure-data
(jabber-caps-try-next jc hash node ver)))
(defun jabber-caps-try-next (jc hash node ver)
(let* ((key (cons hash ver))
(cache-entry (gethash key jabber-caps-cache)))
(when (floatp (car-safe cache-entry))
(let ((next-jid (pop (cdr cache-entry))))
;; Do we know someone else we could ask about this hash?
(if next-jid
(progn
(setf (car cache-entry) (float-time))
(jabber-send-iq
jc next-jid
"get"
`(query ((xmlns . "http://jabber.org/protocol/disco#info")
(node . ,(concat node "#" ver))))
#'jabber-process-caps-info-result (list hash node ver)
#'jabber-process-caps-info-error (list hash node ver)))
;; No, forget about it for now.
(remhash key jabber-caps-cache))))))
;;; Entity Capabilities utility functions
(defun jabber-caps-ver-string (query hash)
;; XEP-0115, section 5.1
;; 1. Initialize an empty string S.
(with-temp-buffer
(let* ((identities (jabber-xml-get-children query 'identity))
(disco-features (mapcar (lambda (f) (jabber-xml-get-attribute f 'var))
(jabber-xml-get-children query 'feature)))
(maybe-forms (jabber-xml-get-children query 'x))
(forms (remove-if-not
(lambda (x)
;; Keep elements that are forms and have a FORM_TYPE,
;; according to XEP-0128.
(and (string= (jabber-xml-get-xmlns x) "jabber:x:data")
(jabber-xdata-formtype x)))
maybe-forms)))
;; 2. Sort the service discovery identities [15] by category
;; and then by type and then by xml:lang (if it exists),
;; formatted as CATEGORY '/' [TYPE] '/' [LANG] '/'
;; [NAME]. [16] Note that each slash is included even if the
;; LANG or NAME is not included (in accordance with XEP-0030,
;; the category and type MUST be included.
(setq identities (sort identities #'jabber-caps-identity-<))
;; 3. For each identity, append the 'category/type/lang/name' to
;; S, followed by the '<' character.
(dolist (identity identities)
(jabber-xml-let-attributes (category type xml:lang name) identity
;; Use `concat' here instead of passing everything to
;; `insert', since `concat' tolerates nil values.
(insert (concat category "/" type "/" xml:lang "/" name "<"))))
;; 4. Sort the supported service discovery features. [17]
(setq disco-features (sort disco-features #'string<))
;; 5. For each feature, append the feature to S, followed by the
;; '<' character.
(dolist (f disco-features)
(insert f "<"))
;; 6. If the service discovery information response includes
;; XEP-0128 data forms, sort the forms by the FORM_TYPE (i.e.,
;; by the XML character data of the <value/> element).
(setq forms (sort forms (lambda (a b)
(string< (jabber-xdata-formtype a)
(jabber-xdata-formtype b)))))
;; 7. For each extended service discovery information form:
(dolist (form forms)
;; Append the XML character data of the FORM_TYPE field's
;; <value/> element, followed by the '<' character.
(insert (jabber-xdata-formtype form) "<")
;; Sort the fields by the value of the "var" attribute.
(let ((fields (sort (jabber-xml-get-children form 'field)
(lambda (a b)
(string< (jabber-xml-get-attribute a 'var)
(jabber-xml-get-attribute b 'var))))))
(dolist (field fields)
;; For each field other than FORM_TYPE:
(unless (string= (jabber-xml-get-attribute field 'var) "FORM_TYPE")
;; Append the value of the "var" attribute, followed by the '<' character.
(insert (jabber-xml-get-attribute field 'var) "<")
;; Sort values by the XML character data of the <value/> element.
(let ((values (sort (mapcar (lambda (value)
(car (jabber-xml-node-children value)))
(jabber-xml-get-children field 'value))
#'string<)))
;; For each <value/> element, append the XML character
;; data, followed by the '<' character.
(dolist (value values)
(insert value "<"))))))))
;; 8. Ensure that S is encoded according to the UTF-8 encoding
;; (RFC 3269 [18]).
(let ((s (encode-coding-string (buffer-string) 'utf-8 t))
(algorithm (cdr (assoc hash jabber-caps-hash-names))))
;; 9. Compute the verification string by hashing S using the
;; algorithm specified in the 'hash' attribute (e.g., SHA-1 as
;; defined in RFC 3174 [19]). The hashed data MUST be generated
;; with binary output and encoded using Base64 as specified in
;; Section 4 of RFC 4648 [20] (note: the Base64 output MUST NOT
;; include whitespace and MUST set padding bits to zero). [21]
(base64-encode-string (jabber-caps--secure-hash algorithm s) t))))
(defun jabber-caps--secure-hash (algorithm string)
(cond
;; `secure-hash' was introduced in Emacs 24
((fboundp 'secure-hash)
(secure-hash algorithm string nil nil t))
((eq algorithm 'sha1)
;; For SHA-1, we can use the `sha1' function.
(sha1 string nil nil t))
(t
(error "Cannot use hash algorithm %s!" algorithm))))
(defun jabber-caps-identity-< (a b)
(let ((a-category (jabber-xml-get-attribute a 'category))
(b-category (jabber-xml-get-attribute b 'category)))
(or (string< a-category b-category)
(and (string= a-category b-category)
(let ((a-type (jabber-xml-get-attribute a 'type))
(b-type (jabber-xml-get-attribute b 'type)))
(or (string< a-type b-type)
(and (string= a-type b-type)
(let ((a-xml:lang (jabber-xml-get-attribute a 'xml:lang))
(b-xml:lang (jabber-xml-get-attribute b 'xml:lang)))
(string< a-xml:lang b-xml:lang)))))))))
;;; Sending Entity Capabilities
(defvar jabber-caps-default-hash-function "sha-1"
"Hash function to use when sending caps in presence stanzas.
The value should be a key in `jabber-caps-hash-names'.")
(defvar jabber-caps-current-hash nil
"The current disco hash we're sending out in presence stanzas.")
(defconst jabber-caps-node "http://emacs-jabber.sourceforge.net")
;;;###autoload
(defun jabber-disco-advertise-feature (feature)
(unless (member feature jabber-advertised-features)
(push feature jabber-advertised-features)
(when jabber-caps-current-hash
(jabber-caps-recalculate-hash)
;; If we're already connected, we need to send updated presence
;; for the new feature.
(mapc #'jabber-send-current-presence jabber-connections))))
(defun jabber-caps-recalculate-hash ()
"Update `jabber-caps-current-hash' for feature list change.
Also update `jabber-disco-info-nodes', so we return results for
the right node."
(let* ((old-hash jabber-caps-current-hash)
(old-node (and old-hash (concat jabber-caps-node "#" old-hash)))
(new-hash
(jabber-caps-ver-string `(query () ,@(jabber-disco-return-client-info))
jabber-caps-default-hash-function))
(new-node (concat jabber-caps-node "#" new-hash)))
(when old-node
(let ((old-entry (assoc old-node jabber-disco-info-nodes)))
(when old-entry
(setq jabber-disco-info-nodes (delq old-entry jabber-disco-info-nodes)))))
(push (list new-node #'jabber-disco-return-client-info nil)
jabber-disco-info-nodes)
(setq jabber-caps-current-hash new-hash)))
;;;###autoload
(defun jabber-caps-presence-element (_jc)
(unless jabber-caps-current-hash
(jabber-caps-recalculate-hash))
(list
`(c ((xmlns . "http://jabber.org/protocol/caps")
(hash . ,jabber-caps-default-hash-function)
(node . ,jabber-caps-node)
(ver . ,jabber-caps-current-hash)))))
;;;###autoload
(eval-after-load "jabber-presence"
'(add-to-list 'jabber-presence-element-functions #'jabber-caps-presence-element))
(provide 'jabber-disco)
;;; arch-tag: 71f5c76f-2956-4ed2-b871-9f5fe198092d

View File

@ -1,245 +0,0 @@
;;; jabber-events.el --- Message events (JEP-0022) implementation
;; Copyright (C) 2005, 2008 Magnus Henoch
;; Author: Magnus Henoch <mange@freemail.hu>
;; This file 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.
;; This file 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 GNU Emacs; see the file COPYING. If not, write to
;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
;; Boston, MA 02111-1307, USA.
(require 'cl)
(defgroup jabber-events nil
"Message events and notifications."
:group 'jabber)
;;; INCOMING
;;; Code for requesting event notifications from others and handling
;;; them.
(defcustom jabber-events-request-these '(offline
delivered
displayed
composing)
"Request these kinds of event notifications from others."
:type '(set (const :tag "Delivered to offline storage" offline)
(const :tag "Delivered to user's client" delivered)
(const :tag "Displayed to user" displayed)
(const :tag "User is typing a reply" composing))
:group 'jabber-events)
(defvar jabber-events-composing-p nil
"Is the other person composing a message?")
(make-variable-buffer-local 'jabber-events-composing-p)
(defvar jabber-events-arrived nil
"In what way has the message reached the recipient?
Possible values are nil (no information available), offline
\(queued for delivery when recipient is online), delivered
\(message has reached the client) and displayed (user is
probably reading the message).")
(make-variable-buffer-local 'jabber-events-arrived)
(defvar jabber-events-message ""
"Human-readable presentation of event information")
(make-variable-buffer-local 'jabber-events-message)
(defun jabber-events-update-message ()
(setq jabber-events-message
(concat (cdr (assq jabber-events-arrived
'((offline . "In offline storage")
(delivered . "Delivered")
(displayed . "Displayed"))))
(when jabber-events-composing-p
" (typing a message)"))))
(add-hook 'jabber-chat-send-hooks 'jabber-events-when-sending)
(defun jabber-events-when-sending (text id)
(setq jabber-events-arrived nil)
(jabber-events-update-message)
`((x ((xmlns . "jabber:x:event"))
,@(mapcar #'list jabber-events-request-these))))
;;; OUTGOING
;;; Code for handling requests for event notifications and providing
;;; them, modulo user preferences.
(defcustom jabber-events-confirm-delivered t
"Send delivery confirmation if requested?"
:group 'jabber-events
:type 'boolean)
(defcustom jabber-events-confirm-displayed t
"Send display confirmation if requested?"
:group 'jabber-events
:type 'boolean)
(defcustom jabber-events-confirm-composing t
"Send notifications about typing a reply?"
:group 'jabber-events
:type 'boolean)
(defvar jabber-events-requested ()
"List of events requested")
(make-variable-buffer-local 'jabber-events-requested)
(defvar jabber-events-last-id nil
"Id of last message received, or nil if none.")
(make-variable-buffer-local 'jabber-events-last-id)
(defvar jabber-events-delivery-confirmed nil
"Has delivery confirmation been sent?")
(make-variable-buffer-local 'jabber-events-delivery-confirmed)
(defvar jabber-events-display-confirmed nil
"Has display confirmation been sent?")
(make-variable-buffer-local 'jabber-events-display-confirmed)
(defvar jabber-events-composing-sent nil
"Has composing notification been sent?
It can be sent and cancelled several times.")
(add-hook 'window-configuration-change-hook
'jabber-events-confirm-display)
(defun jabber-events-confirm-display ()
"Send display confirmation if appropriate.
That is, if user allows it, if the other user requested it,
and it hasn't been sent before."
(walk-windows #'jabber-events-confirm-display-in-window))
(defun jabber-events-confirm-display-in-window (window)
(with-current-buffer (window-buffer window)
(when (and jabber-events-confirm-displayed
(not jabber-events-display-confirmed)
(memq 'displayed jabber-events-requested)
;; XXX: if jabber-events-requested is non-nil, how can
;; jabber-chatting-with be nil? See
;; http://sourceforge.net/tracker/index.php?func=detail&aid=1872560&group_id=88346&atid=586350
jabber-chatting-with
;; don't send to bare jids
(jabber-jid-resource jabber-chatting-with))
(jabber-send-sexp
jabber-buffer-connection
`(message
((to . ,jabber-chatting-with))
(x ((xmlns . "jabber:x:event"))
(displayed)
(id () ,jabber-events-last-id))))
(setq jabber-events-display-confirmed t))))
(defun jabber-events-after-change ()
(let ((composing-now (not (= (point-max) jabber-point-insert))))
(when (and jabber-events-confirm-composing
jabber-chatting-with
(not (eq composing-now jabber-events-composing-sent)))
(jabber-send-sexp
jabber-buffer-connection
`(message
((to . ,jabber-chatting-with))
(x ((xmlns . "jabber:x:event"))
,@(if composing-now '((composing)) nil)
(id () ,jabber-events-last-id))))
(setq jabber-events-composing-sent composing-now))))
;;; COMMON
;; Add function last in chain, so a chat buffer is already created.
(add-to-list 'jabber-message-chain 'jabber-handle-incoming-message-events t)
(defun jabber-handle-incoming-message-events (jc xml-data)
(when (and (not (jabber-muc-message-p xml-data))
(get-buffer (jabber-chat-get-buffer (jabber-xml-get-attribute xml-data 'from))))
(with-current-buffer (jabber-chat-get-buffer (jabber-xml-get-attribute xml-data 'from))
(let ((x (find "jabber:x:event"
(jabber-xml-get-children xml-data 'x)
:key #'(lambda (x) (jabber-xml-get-attribute x 'xmlns))
:test #'string=)))
(cond
;; If we get an error message, we shouldn't report any
;; events, as the requests are mirrored from us.
((string= (jabber-xml-get-attribute xml-data 'type) "error")
(remove-hook 'post-command-hook 'jabber-events-after-change t)
(setq jabber-events-requested nil))
;; If there's a body, it's not an incoming message event.
((jabber-xml-get-children xml-data 'body)
;; User is done composing, obviously.
(setq jabber-events-composing-p nil)
(jabber-events-update-message)
;; Reset variables
(setq jabber-events-display-confirmed nil)
(setq jabber-events-delivery-confirmed nil)
;; User requests message events
(setq jabber-events-requested
;; There might be empty strings in the XML data,
;; which car chokes on. Having nil values in
;; the list won't hurt, therefore car-safe.
(mapcar #'car-safe
(jabber-xml-node-children x)))
(setq jabber-events-last-id (jabber-xml-get-attribute
xml-data 'id))
;; Send notifications we already know about
(flet ((send-notification
(type)
(jabber-send-sexp
jc
`(message
((to . ,(jabber-xml-get-attribute xml-data 'from)))
(x ((xmlns . "jabber:x:event"))
(,type)
(id () ,jabber-events-last-id))))))
;; Send delivery confirmation if appropriate
(when (and jabber-events-confirm-delivered
(memq 'delivered jabber-events-requested))
(send-notification 'delivered)
(setq jabber-events-delivery-confirmed t))
;; Send display confirmation if appropriate
(when (and jabber-events-confirm-displayed
(get-buffer-window (current-buffer) 'visible)
(memq 'displayed jabber-events-requested))
(send-notification 'displayed)
(setq jabber-events-display-confirmed t))
;; Set up hooks for composition notification
(when (and jabber-events-confirm-composing
(memq 'composing jabber-events-requested))
(add-hook 'post-command-hook 'jabber-events-after-change
nil t))))
(t
;; So it has no body. If it's a message event,
;; the <x/> node should be the only child of the
;; message, and it should contain an <id/> node.
;; We check the latter.
(when (and x (jabber-xml-get-children x 'id))
;; Currently we don't care about the <id/> node.
;; There's only one node except for the id.
(unless
(dolist (possible-node '(offline delivered displayed))
(when (jabber-xml-get-children x possible-node)
(setq jabber-events-arrived possible-node)
(jabber-events-update-message)
(return t)))
;; Or maybe even zero, which is a negative composing node.
(setq jabber-events-composing-p
(not (null (jabber-xml-get-children x 'composing))))
(jabber-events-update-message)))))))))
(provide 'jabber-events)
;; arch-tag: 7b6e61fe-a9b3-11d9-afca-000a95c2fcd0

View File

@ -1,251 +0,0 @@
;;; jabber-export.el --- export Jabber roster to file
;; Copyright (C) 2005, 2007 Magnus Henoch
;; Author: Magnus Henoch <mange@freemail.hu>
;; This file 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.
;; This file 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 GNU Emacs; see the file COPYING. If not, write to
;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
;; Boston, MA 02111-1307, USA.
(require 'cl)
(defvar jabber-export-roster-widget nil)
(defvar jabber-import-subscription-p-widget nil)
;;;###autoload
(defun jabber-export-roster (jc)
"Export roster for connection JC."
(interactive (list (jabber-read-account)))
(let ((state-data (fsm-get-state-data jc)))
(jabber-export-roster-do-it
(jabber-roster-to-sexp (plist-get state-data :roster)))))
(defun jabber-export-roster-do-it (roster)
"Create buffer from which ROSTER can be exported to a file."
(interactive)
(with-current-buffer (get-buffer-create "Export roster")
(jabber-init-widget-buffer nil)
(widget-insert (jabber-propertize "Export roster\n"
'face 'jabber-title-large))
(widget-insert "You are about to save your roster to a file. Here
you can edit it before saving. Changes done here will
not affect your actual roster.
")
(widget-create 'push-button :notify #'jabber-export-save "Save to file")
(widget-insert " ")
(widget-create 'push-button :notify #'jabber-export-remove-regexp "Remove by regexp")
(widget-insert "\n\n")
(make-local-variable 'jabber-export-roster-widget)
(jabber-export-display roster)
(widget-setup)
(widget-minor-mode 1)
(goto-char (point-min))
(switch-to-buffer (current-buffer))))
;;;###autoload
(defun jabber-import-roster (jc file)
"Create buffer for roster import for connection JC from FILE."
(interactive (list (jabber-read-account)
(read-file-name "Import roster from file: ")))
(let ((roster
(with-temp-buffer
(let ((coding-system-for-read 'utf-8))
(jabber-roster-xml-to-sexp
(car (xml-parse-file file)))))))
(with-current-buffer (get-buffer-create "Import roster")
(setq jabber-buffer-connection jc)
(jabber-init-widget-buffer nil)
(widget-insert (jabber-propertize "Import roster\n"
'face 'jabber-title-large))
(widget-insert "You are about to import the contacts below to your roster.
")
(make-local-variable 'jabber-import-subscription-p-widget)
(setq jabber-import-subscription-p-widget
(widget-create 'checkbox))
(widget-insert " Adjust subscriptions\n")
(widget-create 'push-button :notify #'jabber-import-doit "Import to roster")
(widget-insert " ")
(widget-create 'push-button :notify #'jabber-export-remove-regexp "Remove by regexp")
(widget-insert "\n\n")
(make-local-variable 'jabber-export-roster-widget)
(jabber-export-display roster)
(widget-setup)
(widget-minor-mode 1)
(goto-char (point-min))
(switch-to-buffer (current-buffer)))))
(defun jabber-export-remove-regexp (&rest ignore)
(let* ((value (widget-value jabber-export-roster-widget))
(length-before (length value))
(regexp (read-string "Remove JIDs matching regexp: ")))
(setq value (delete-if
#'(lambda (a)
(string-match regexp (nth 0 a)))
value))
(widget-value-set jabber-export-roster-widget value)
(widget-setup)
(message "%d items removed" (- length-before (length value)))))
(defun jabber-export-save (&rest ignore)
"Export roster to file."
(let ((items (mapcar #'jabber-roster-sexp-to-xml (widget-value jabber-export-roster-widget)))
(coding-system-for-write 'utf-8))
(with-temp-file (read-file-name "Export roster to file: ")
(insert "<iq xmlns='jabber:client'><query xmlns='jabber:iq:roster'>\n")
(dolist (item items)
(insert (jabber-sexp2xml item) "\n"))
(insert "</query></iq>\n"))
(message "Roster saved")))
(defun jabber-import-doit (&rest ignore)
"Import roster being edited in widget."
(let* ((state-data (fsm-get-state-data jabber-buffer-connection))
(jabber-roster (plist-get state-data :roster))
roster-delta)
(dolist (n (widget-value jabber-export-roster-widget))
(let* ((jid (nth 0 n))
(name (and (not (zerop (length (nth 1 n))))
(nth 1 n)))
(subscription (nth 2 n))
(groups (nth 3 n))
(jid-symbol (jabber-jid-symbol jid))
(in-roster-p (memq jid-symbol jabber-roster))
(jid-name (and in-roster-p (get jid-symbol 'name)))
(jid-subscription (and in-roster-p (get jid-symbol 'subscription)))
(jid-groups (and in-roster-p (get jid-symbol 'groups))))
;; Do we need to change the roster?
(when (or
;; If the contact is not in the roster already,
(not in-roster-p)
;; or if the import introduces a name,
(and name (not jid-name))
;; or changes a name,
(and name jid-name (not (string= name jid-name)))
;; or introduces new groups.
(set-difference groups jid-groups :test #'string=))
(push (jabber-roster-sexp-to-xml
(list jid (or name jid-name) nil (union groups jid-groups :test #'string=))
t)
roster-delta))
;; And adujst subscription.
(when (widget-value jabber-import-subscription-p-widget)
(let ((want-to (member subscription '("to" "both")))
(want-from (member subscription '("from" "both")))
(have-to (member jid-subscription '("to" "both")))
(have-from (member jid-subscription '("from" "both"))))
(flet ((request-subscription
(type)
(jabber-send-sexp jabber-buffer-connection
`(presence ((to . ,jid)
(type . ,type))))))
(cond
((and want-to (not have-to))
(request-subscription "subscribe"))
((and have-to (not want-to))
(request-subscription "unsubscribe")))
(cond
((and want-from (not have-from))
;; not much to do here
)
((and have-from (not want-from))
(request-subscription "unsubscribed"))))))))
(when roster-delta
(jabber-send-iq jabber-buffer-connection
nil "set"
`(query ((xmlns . "jabber:iq:roster")) ,@roster-delta)
#'jabber-report-success "Roster import"
#'jabber-report-success "Roster import"))))
(defun jabber-roster-to-sexp (roster)
"Convert ROSTER to simpler sexp format.
Return a list, where each item is a vector:
\[jid name subscription groups]
where groups is a list of strings."
(mapcar
#'(lambda (n)
(list
(symbol-name n)
(or (get n 'name) "")
(get n 'subscription)
(get n 'groups)))
roster))
(defun jabber-roster-sexp-to-xml (sexp &optional omit-subscription)
"Convert SEXP to XML format.
Return an XML node."
`(item ((jid . ,(nth 0 sexp))
,@(let ((name (nth 1 sexp)))
(unless (zerop (length name))
`((name . ,name))))
,@(unless omit-subscription
`((subscription . ,(nth 2 sexp)))))
,@(mapcar
#'(lambda (g)
(list 'group nil g))
(nth 3 sexp))))
(defun jabber-roster-xml-to-sexp (xml-data)
"Convert XML-DATA to simpler sexp format.
XML-DATA is an <iq> node with a <query xmlns='jabber:iq:roster'> child.
See `jabber-roster-to-sexp' for description of output format."
(assert (eq (jabber-xml-node-name xml-data) 'iq))
(let ((query (car (jabber-xml-get-children xml-data 'query))))
(assert query)
(mapcar
#'(lambda (n)
(list
(jabber-xml-get-attribute n 'jid)
(or (jabber-xml-get-attribute n 'name) "")
(jabber-xml-get-attribute n 'subscription)
(mapcar
#'(lambda (g)
(car (jabber-xml-node-children g)))
(jabber-xml-get-children n 'group))))
(jabber-xml-get-children query 'item))))
(defun jabber-export-display (roster)
(setq jabber-export-roster-widget
(widget-create
'(repeat
:tag "Roster"
(list :format "%v"
(string :tag "JID")
(string :tag "Name")
(choice :tag "Subscription"
(const "none")
(const "both")
(const "to")
(const "from"))
(repeat :tag "Groups"
(string :tag "Group"))))
:value roster)))
(provide 'jabber-export)
;;; arch-tag: 9c6b94a9-290a-4c0f-9286-72bd9c1fb8a3

View File

@ -1,731 +0,0 @@
;;; hexrgb.el --- Functions to manipulate colors, including RGB hex strings.
;;
;; Filename: hexrgb.el
;; Description: Functions to manipulate colors, including RGB hex strings.
;; Author: Drew Adams
;; Maintainer: Drew Adams
;; Copyright (C) 2004-2009, Drew Adams, all rights reserved.
;; Created: Mon Sep 20 22:58:45 2004
;; Version: 21.0
;; Last-Updated: Sat Nov 14 15:55:15 2009 (-0800)
;; By: dradams
;; Update #: 732
;; URL: http://www.emacswiki.org/cgi-bin/wiki/hexrgb.el
;; Keywords: number, hex, rgb, color, background, frames, display
;; Compatibility: GNU Emacs: 20.x, 21.x, 22.x, 23.x
;;
;; Features that might be required by this library:
;;
;; None
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;;; Commentary:
;;
;; Functions to manipulate colors, including RGB hex strings.
;;
;; This library provides functions for converting between RGB (red,
;; green, blue) color components and HSV (hue, saturation, value)
;; color components. It helps you convert among Emacs color values
;; (whole numbers from 0 through 65535), RGB and HSV floating-point
;; components (0.0 through 1.0), Emacs color-name strings (such as
;; "blue"), and hex RGB color strings (such as "#FC43A7912").
;;
;; An RGB hex string, such as used as a frame `background-color'
;; property, is a string of 1 + (3 * n) characters, the first of
;; which is "#". The other characters are hexadecimal digits, in
;; three groups representing (from the left): red, green, and blue
;; hex codes.
;;
;; Constants defined here:
;;
;; `hexrgb-defined-colors', `hexrgb-defined-colors-alist',
;; `hexrgb-defined-colors-no-dups',
;; `hexrgb-defined-colors-no-dups-alist'.
;;
;; Options defined here:
;;
;; `hexrgb-canonicalize-defined-colors-flag'.
;;
;; Commands defined here:
;;
;; `hexrgb-blue', `hexrgb-complement', `hexrgb-green',
;; `hexrgb-hue', `hexrgb-read-color', `hexrgb-red',
;; `hexrgb-saturation', `hexrgb-value'.
;;
;; Non-interactive functions defined here:
;;
;; `hexrgb-approx-equal', `hexrgb-canonicalize-defined-colors',
;; `hexrgb-color-name-to-hex', `hexrgb-color-values-to-hex',
;; `hexrgb-color-value-to-float', `hexrgb-defined-colors',
;; `hexrgb-defined-colors-alist',
;; `hexrgb-delete-whitespace-from-string',
;; `hexrgb-float-to-color-value', `hexrgb-hex-char-to-integer',
;; `hexrgb-hex-to-color-values', `hexrgb-hex-to-hsv',
;; `hexrgb-hex-to-rgb', `hexrgb-hsv-to-hex', `hexrgb-hex-to-int',
;; `hexrgb-hsv-to-rgb', `hexrgb-increment-blue',
;; `hexrgb-increment-equal-rgb', `hexrgb-increment-green',
;; `hexrgb-increment-hex', `hexrgb-increment-red',
;; `hexrgb-int-to-hex', `hexrgb-rgb-hex-string-p',
;; `hexrgb-rgb-to-hex', `hexrgb-rgb-to-hsv'.
;;
;;
;; Add this to your initialization file (~/.emacs or ~/_emacs):
;;
;; (require 'hexrgb)
;;
;; Do not try to use this library without a window manager.
;; That is, do not use this with `emacs -nw'.
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;;; Change log:
;;
;; 2009/11/14 dadams
;; hexrgb-rgb-to-hsv: Corrected hue when > 1.0. Use strict inequality for hue limit tests.
;; hexrgb-approx-equal: Convert RFUZZ and AFUZZ to their absolute values.
;; 2009/11/03 dadams
;; Added: hexrgb-delete-whitespace-from-string, hexrgb-canonicalize-defined-colors,
;; hexrgb-defined-colors(-no-dups)(-alist), hexrgb-canonicalize-defined-colors-flag.
;; hexrgb-read-color: Use function hexrgb-defined-colors-alist, not the constant.
;; 2008/12/25 dadams
;; hexrgb-rgb-to-hsv:
;; Replace (not (equal 0.0e+NaN saturation)) by standard test (= saturation saturation).
;; Thx to Michael Heerdegen for the bug report.
;; 2008-10-17 dadams
;; hexrgb-defined-colors(-alist): Prevent load-time error if user tries to use emacs -nw.
;; 2007/12/30 dadams
;; Added: hexrgb-hex-to-color-values.
;; 2007/10/20 dadams
;; hexrgb-read-color: Treat pseudo colors too (e.g. *point foreground*).
;; 2007/01/21 dadams
;; hexrgb-read-color: Error if empty string (and not allow-empty-name-p).
;; 2006/06/06 dadams
;; Added: hexrgb-defined-colors(-alist). Use instead of (x-defined-colors).
;; hexrgb-(red|green|blue): Added interactive specs.
;; 2006/06/04 dadams
;; hexrgb-read-color: Added optional arg allow-empty-name-p.
;; 2006/06/02 dadams
;; Added: hexrgb-rgb-hex-string-p. Used it.
;; 2006/05/30 dadams
;; Added: hexrgb-hex-to-(hsv|rgb), hexrgb-hsv-to-hex, hexrgb-color-name-to-hex,
;; hexrgb-complement, hexrgb-read-color, hexrgb-hue, hexrgb-saturation,
;; hexrgb-value, hexrgb-red, hexrgb-blue, hexrgb-green.
;; approx-equal: Add optional fuzz factor arguments. Changed the algorithm.
;; Renamed: approx-equal to hexrgb-approx-equal.
;; hexrgb-rgb-to-hsv: Changed test from < to <=: (when (<= hue 0.0)...).
;; hexrgb-hsv-to-rgb: Treat hue = 0.0 (int 0) the same as hue = 1.0 (int 6).
;; hexrgb-rgb-to-hex, hexrgb-increment-hex: Corrected doc strings.
;; 2006/05/22 dadams
;; Added: hexrgb-hsv-to-hex, hexrgb-rgb-to-hex. Require cl.el when byte-compile.
;; 2005/08/09 dadams
;; hexrgb-rgb-to-hsv: Side-stepped Emacs-20 bug in comparing NaN.
;; hexrgb-increment-*: Added optional arg wrap-p.
;; hexrgb-increment-hex: Prevent wrap if not wrap-p.
;; 2005/08/02 dadams
;; hexrgb-rgb-to-hes: Bug fix: If delta is zero, then so are hue and saturation.
;; 2005/06/24 dadams
;; hexrgb-rgb-to-hsv: Bug fix: test for NaN (e.g. on divide by zero).
;; 2005/02/08 dadams
;; hexrgb-hsv-to-rgb: Bug fix (typo: p, q -> pp, qq; added ww).
;; 2005/01/09 dadams
;; hexrgb-int-to-hex: Fixed bug in hexrgb-int-to-hex: nb-digits not respected.
;; Added: hexrgb-hsv-to-rgb, hexrgb-rgb-to-hsv, approx-equal.
;; Renamed old hexrgb-increment-value to hexrgb-increment-equal-rgb.
;; 2005/01/05 dadams
;; hexrgb-int-to-hex: Used a suggestion from Juri Linkov.
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; 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, 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; see the file COPYING. If not, write to
;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth
;; Floor, Boston, MA 02110-1301, USA.
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;;; Code:
(eval-when-compile (require 'cl)) ;; case; plus, for Emacs < 20: when, unless
;; Unless you first load `hexrgb.el', then either `palette.el' or `eyedropper.el', you will get
;; warnings about variables and functions with prefix `eyedrop-' when you byte-compile
;; `hexrgb.el'. You can ignore these warnings.
(defvar eyedrop-picked-foreground)
(defvar eyedrop-picked-background)
;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;###autoload
(eval-and-compile
(defun hexrgb-canonicalize-defined-colors (list)
"Copy of LIST with color names canonicalized.
LIST is a list of color names (strings).
Canonical names are lowercase, with no whitespace.
There are no duplicate names."
(let ((tail list)
this new)
(while tail
(setq this (car tail)
this (hexrgb-delete-whitespace-from-string (downcase this) 0 (length this)))
(unless (member this new) (push this new))
(pop tail))
(nreverse new)))
(defun hexrgb-delete-whitespace-from-string (string &optional from to)
"Remove whitespace from substring of STRING from FROM to TO.
If FROM is nil, then start at the beginning of STRING (FROM = 0).
If TO is nil, then end at the end of STRING (TO = length of STRING).
FROM and TO are zero-based indexes into STRING.
Character FROM is affected (possibly deleted). Character TO is not."
(setq from (or from 0)
to (or to (length string)))
(with-temp-buffer
(insert string)
(goto-char (+ from (point-min)))
(let ((count from)
char)
(while (and (not (eobp)) (< count to))
(setq char (char-after))
(if (memq char '(?\ ?\t ?\n)) (delete-char 1) (forward-char 1))
(setq count (1+ count)))
(buffer-string)))))
;;;###autoload
(defconst hexrgb-defined-colors (eval-when-compile (and window-system (x-defined-colors)))
"List of all supported colors.")
;;;###autoload
(defconst hexrgb-defined-colors-no-dups
(eval-when-compile
(and window-system (hexrgb-canonicalize-defined-colors (x-defined-colors))))
"List of all supported color names, with no duplicates.
Names are all lowercase, without any spaces.")
;;;###autoload
(defconst hexrgb-defined-colors-alist
(eval-when-compile (and window-system (mapcar #'list (x-defined-colors))))
"Alist of all supported color names, for use in completion.
See also `hexrgb-defined-colors-no-dups-alist', which is the same
thing, but without any duplicates, such as \"light blue\" and
\"LightBlue\".")
;;;###autoload
(defconst hexrgb-defined-colors-no-dups-alist
(eval-when-compile
(and window-system
(mapcar #'list (hexrgb-canonicalize-defined-colors (x-defined-colors)))))
"Alist of all supported color names, with no duplicates, for completion.
Names are all lowercase, without any spaces.")
;;;###autoload
(defcustom hexrgb-canonicalize-defined-colors-flag t
"*Non-nil means remove duplicate color names.
Names are considered duplicates if they are the same when abstracting
from whitespace and letter case."
:type 'boolean
:group 'Icicles :group 'doremi-frame-commands :group 'faces :group 'convenience)
;; You should use these two functions, not the constants, so users can change
;; the behavior by customizing `hexrgb-canonicalize-defined-colors-flag'.
(defun hexrgb-defined-colors ()
"List of supported color names.
If `hexrgb-canonicalize-defined-colors-flag' is non-nil, then names
are lowercased, whitespace is removed, and there are no duplicates."
(if hexrgb-canonicalize-defined-colors-flag
hexrgb-defined-colors-no-dups
hexrgb-defined-colors))
(defun hexrgb-defined-colors-alist ()
"Alist of supported color names. Usable for completion.
If `hexrgb-canonicalize-defined-colors-flag' is non-nil, then names
are lowercased, whitespace is removed, and there are no duplicates."
(if hexrgb-canonicalize-defined-colors-flag
hexrgb-defined-colors-no-dups-alist
hexrgb-defined-colors-alist))
;; RMS added this function to Emacs (23) as `read-color', with some feature loss.
;;;###autoload
(defun hexrgb-read-color (&optional convert-to-RGB-p allow-empty-name-p prompt)
"Read a color name or RGB hex value: #RRRRGGGGBBBB.
Completion is available for color names, but not for RGB hex strings.
If you input an RGB hex string, it must have the form #XXXXXXXXXXXX or
XXXXXXXXXXXX, where each X is a hex digit. The number of Xs must be a
multiple of 3, with the same number of Xs for each of red, green, and
blue. The order is red, green, blue.
Color names that are normally considered equivalent are canonicalized:
They are lowercased, whitespace is removed, and duplicates are
eliminated. E.g. \"LightBlue\" and \"light blue\" are both replaced
by \"lightblue\". If you do not want this behavior, but want to
choose names that might contain whitespace or uppercase letters, then
customize option `hexrgb-canonicalize-defined-colors-flag' to nil.
In addition to standard color names and RGB hex values, the following
are available as color candidates. In each case, the corresponding
color is used.
* `*copied foreground*' - last copied foreground, if available
* `*copied background*' - last copied background, if available
* `*mouse-2 foreground*' - foreground where you click `mouse-2'
* `*mouse-2 background*' - background where you click `mouse-2'
* `*point foreground*' - foreground under the cursor
* `*point background*' - background under the cursor
\(You can copy a color using eyedropper commands such as
`eyedrop-pick-foreground-at-mouse'.)
Checks input to be sure it represents a valid color. If not, raises
an error (but see exception for empty input with non-nil
ALLOW-EMPTY-NAME-P).
Interactively, or with optional arg CONVERT-TO-RGB-P non-nil, converts
an input color name to an RGB hex string. Returns the RGB hex string.
Optional arg ALLOW-EMPTY-NAME-P controls what happens if you enter an
empty color name (that is, you just hit `RET'). If non-nil, then
`hexrgb-read-color' returns an empty color name, \"\". If nil, then
it raises an error. Programs must test for \"\" if ALLOW-EMPTY-NAME-P
is non-nil. They can then perform an appropriate action in case of
empty input.
Optional arg PROMPT is the prompt. Nil means use a default prompt."
(interactive "p") ; Always convert to RGB interactively.
(let* ((completion-ignore-case t)
;; Free variables here: `eyedrop-picked-foreground', `eyedrop-picked-background'.
;; They are defined in library `palette.el' or library `eyedropper.el'.
(colors (if (fboundp 'eyedrop-foreground-at-point)
(append (and eyedrop-picked-foreground
'(("*copied foreground*")))
(and eyedrop-picked-background
'(("*copied background*")))
'(("*mouse-2 foreground*")
("*mouse-2 background*")
("*point foreground*") ("*point background*"))
(hexrgb-defined-colors-alist))
(hexrgb-defined-colors-alist)))
(color (completing-read (or prompt "Color (name or #R+G+B+): ")
colors))
hex-string)
(when (fboundp 'eyedrop-foreground-at-point)
(cond ((string= "*copied foreground*" color) (setq color eyedrop-picked-foreground))
((string= "*copied background*" color) (setq color eyedrop-picked-background))
((string= "*point foreground*" color) (setq color (eyedrop-foreground-at-point)))
((string= "*point background*" color) (setq color (eyedrop-background-at-point)))
((string= "*mouse-2 foreground*" color)
(setq color (prog1 (eyedrop-foreground-at-mouse
(read-event "Click `mouse-2' to choose foreground color - "))
(read-event)))) ; Discard mouse up event.
((string= "*mouse-2 background*" color)
(setq color (prog1 (eyedrop-background-at-mouse
(read-event "Click `mouse-2' to choose background color - "))
(read-event)))))) ; Discard mouse up event.
(setq hex-string (or (string-match "^#\\([a-fA-F0-9][a-fA-F0-9][a-fA-F0-9]\\)+$" color)
(and (string-match "^\\([a-fA-F0-9][a-fA-F0-9][a-fA-F0-9]\\)+$" color)
t)))
(if (and allow-empty-name-p (string= "" color))
""
(when (and hex-string (not (eq 0 hex-string)))
(setq color (concat "#" color))) ; No #; add it.
(unless hex-string
(when (or (string= "" color)
(not (if (fboundp 'test-completion) ; Not defined in Emacs 20.
(test-completion color colors)
(try-completion color colors))))
(error "No such color: %S" color))
(when convert-to-RGB-p (setq color (hexrgb-color-name-to-hex color))))
(when (interactive-p) (message "Color: `%s'" color))
color)))
;;;###autoload
(defun hexrgb-rgb-hex-string-p (color &optional laxp)
"Non-nil if COLOR is an RGB string #XXXXXXXXXXXX.
Each X is a hex digit. The number of Xs must be a multiple of 3, with
the same number of Xs for each of red, green, and blue.
Non-nil optional arg LAXP means that the initial `#' is optional. In
that case, for a valid string of hex digits: when # is present 0 is
returned; otherwise, t is returned."
(or (string-match "^#\\([a-fA-F0-9][a-fA-F0-9][a-fA-F0-9]\\)+$" color)
(and laxp (string-match "^\\([a-fA-F0-9][a-fA-F0-9][a-fA-F0-9]\\)+$" color) t)))
;;;###autoload
(defun hexrgb-complement (color)
"Return the color that is the complement of COLOR."
(interactive (list (hexrgb-read-color)))
(setq color (hexrgb-color-name-to-hex color))
(let ((red (hexrgb-red color))
(green (hexrgb-green color))
(blue (hexrgb-blue color)))
(setq color (hexrgb-rgb-to-hex (- 1.0 red) (- 1.0 green) (- 1.0 blue))))
(when (interactive-p) (message "Complement: `%s'" color))
color)
;;;###autoload
(defun hexrgb-hue (color)
"Return the hue component of COLOR, in range 0 to 1 inclusive.
COLOR is a color name or hex RGB string that starts with \"#\"."
(interactive (list (hexrgb-read-color)))
(setq color (hexrgb-color-name-to-hex color))
(car (hexrgb-rgb-to-hsv (hexrgb-red color) (hexrgb-green color) (hexrgb-blue color))))
;;;###autoload
(defun hexrgb-saturation (color)
"Return the saturation component of COLOR, in range 0 to 1 inclusive.
COLOR is a color name or hex RGB string that starts with \"#\"."
(interactive (list (hexrgb-read-color)))
(setq color (hexrgb-color-name-to-hex color))
(cadr (hexrgb-rgb-to-hsv (hexrgb-red color) (hexrgb-green color) (hexrgb-blue color))))
;;;###autoload
(defun hexrgb-value (color)
"Return the value component of COLOR, in range 0 to 1 inclusive.
COLOR is a color name or hex RGB string that starts with \"#\"."
(interactive (list (hexrgb-read-color)))
(setq color (hexrgb-color-name-to-hex color))
(caddr (hexrgb-rgb-to-hsv (hexrgb-red color) (hexrgb-green color) (hexrgb-blue color))))
;;;###autoload
(defun hexrgb-red (color)
"Return the red component of COLOR, in range 0 to 1 inclusive.
COLOR is a color name or hex RGB string that starts with \"#\"."
(interactive (list (hexrgb-read-color)))
(setq color (hexrgb-color-name-to-hex color))
(/ (hexrgb-hex-to-int (substring color 1 (1+ (/ (1- (length color)) 3))))
(expt 16.0 (/ (1- (length color)) 3.0))))
;;;###autoload
(defun hexrgb-green (color)
"Return the green component of COLOR, in range 0 to 1 inclusive.
COLOR is a color name or hex RGB string that starts with \"#\"."
(interactive (list (hexrgb-read-color)))
(setq color (hexrgb-color-name-to-hex color))
(let* ((len (/ (1- (length color)) 3))
(start (1+ len)))
(/ (hexrgb-hex-to-int (substring color start (+ start len)))
(expt 16.0 (/ (1- (length color)) 3.0)))))
;;;###autoload
(defun hexrgb-blue (color)
"Return the blue component of COLOR, in range 0 to 1 inclusive.
COLOR is a color name or hex RGB string that starts with \"#\"."
(interactive (list (hexrgb-read-color)))
(setq color (hexrgb-color-name-to-hex color))
(let* ((len (/ (1- (length color)) 3))
(start (+ 1 len len)))
(/ (hexrgb-hex-to-int (substring color start (+ start len)))
(expt 16.0 (/ (1- (length color)) 3.0)))))
;;;###autoload
(defun hexrgb-rgb-to-hsv (red green blue)
"Convert RED, GREEN, BLUE components to HSV (hue, saturation, value).
Each input component is 0.0 to 1.0, inclusive.
Returns a list of HSV components of value 0.0 to 1.0, inclusive."
(let* ((min (min red green blue))
(max (max red green blue))
(value max)
(delta (- max min))
hue saturation)
(if (hexrgb-approx-equal 0.0 delta)
(setq hue 0.0
saturation 0.0) ; Gray scale - no color; only value.
(if (and (condition-case nil
(setq saturation (/ delta max))
(arith-error nil))
;; Must be a number, not a NaN. The standard test for a NaN is (not (= N N)),
;; but an Emacs 20 bug makes (= N N) return t for a NaN also.
(or (< emacs-major-version 21) (= saturation saturation)))
(if (hexrgb-approx-equal 0.0 saturation)
(setq hue 0.0
saturation 0.0) ; Again, no color; only value.
;; Color
(setq hue (if (hexrgb-approx-equal red max)
(/ (- green blue) delta) ; Between yellow & magenta.
(if (hexrgb-approx-equal green max)
(+ 2.0 (/ (- blue red) delta)) ; Between cyan & yellow.
(+ 4.0 (/ (- red green) delta)))) ; Between magenta & cyan.
hue (/ hue 6.0))
;; (when (<= hue 0.0) (setq hue (+ hue 1.0))) ; $$$$$$
;; (when (>= hue 1.0) (setq hue (- hue 1.0)))) ; $$$$$$
(when (< hue 0.0) (setq hue (+ hue 1.0)))
(when (> hue 1.0) (setq hue (- hue 1.0))))
(setq hue 0.0 ; Div by zero (max=0): H:=0, S:=0. (Hue undefined.)
saturation 0.0)))
(list hue saturation value)))
;;;###autoload
(defun hexrgb-hsv-to-rgb (hue saturation value)
"Convert HUE, SATURATION, VALUE components to RGB (red, green, blue).
Each input component is 0.0 to 1.0, inclusive.
Returns a list of RGB components of value 0.0 to 1.0, inclusive."
(let (red green blue int-hue fract pp qq tt ww)
(if (hexrgb-approx-equal 0.0 saturation)
(setq red value
green value
blue value) ; Gray
(setq hue (* hue 6.0) ; Sectors: 0 to 5
int-hue (floor hue)
fract (- hue int-hue)
pp (* value (- 1 saturation))
qq (* value (- 1 (* saturation fract)))
ww (* value (- 1 (* saturation (- 1 (- hue int-hue))))))
(case int-hue
((0 6) (setq red value
green ww
blue pp))
(1 (setq red qq
green value
blue pp))
(2 (setq red pp
green value
blue ww))
(3 (setq red pp
green qq
blue value))
(4 (setq red ww
green pp
blue value))
(otherwise (setq red value
green pp
blue qq))))
(list red green blue)))
;;;###autoload
(defun hexrgb-hsv-to-hex (hue saturation value)
"Return the hex RBG color string for inputs HUE, SATURATION, VALUE.
The inputs are each in the range 0 to 1.
The output string is of the form \"#RRRRGGGGBBBB\"."
(hexrgb-color-values-to-hex
(mapcar (lambda (x) (floor (* x 65535.0))) (hexrgb-hsv-to-rgb hue saturation value))))
;;;###autoload
(defun hexrgb-rgb-to-hex (red green blue)
"Return the hex RBG color string for inputs RED, GREEN, BLUE.
The inputs are each in the range 0 to 1.
The output string is of the form \"#RRRRGGGGBBBB\"."
(hexrgb-color-values-to-hex
(mapcar (lambda (x) (floor (* x 65535.0))) (list red green blue))))
;;;###autoload
(defun hexrgb-hex-to-hsv (color)
"Return a list of HSV (hue, saturation, value) color components.
Each component is a value from 0.0 to 1.0, inclusive.
COLOR is a color name or a hex RGB string that starts with \"#\" and
is followed by an equal number of hex digits for red, green, and blue
components."
(let ((rgb-components (hexrgb-hex-to-rgb color)))
(apply #'hexrgb-rgb-to-hsv rgb-components)))
;;;###autoload
(defun hexrgb-hex-to-rgb (color)
"Return a list of RGB (red, green, blue) color components.
Each component is a value from 0.0 to 1.0, inclusive.
COLOR is a color name or a hex RGB string that starts with \"#\" and
is followed by an equal number of hex digits for red, green, and blue
components."
(unless (hexrgb-rgb-hex-string-p color) (setq color (hexrgb-color-name-to-hex color)))
(let ((len (/ (1- (length color)) 3)))
(list (/ (hexrgb-hex-to-int (substring color 1 (1+ len))) 65535.0)
(/ (hexrgb-hex-to-int (substring color (1+ len) (+ 1 len len))) 65535.0)
(/ (hexrgb-hex-to-int (substring color (+ 1 len len))) 65535.0))))
;;;###autoload
(defun hexrgb-color-name-to-hex (color)
"Return the RGB hex string for the COLOR name, starting with \"#\".
If COLOR is already a string starting with \"#\", then just return it."
(let ((components (x-color-values color)))
(unless components (error "No such color: %S" color))
(unless (hexrgb-rgb-hex-string-p color)
(setq color (hexrgb-color-values-to-hex components))))
color)
;; Just hard-code 4 as the number of hex digits, since `x-color-values'
;; seems to produce appropriate integer values for this value.
;;
;; Color "components" would be better in the name than color "value"
;; but this name follows the Emacs tradition (e.g. `x-color-values',
;; 'ps-color-values', `ps-e-x-color-values').
;;;###autoload
(defun hexrgb-color-values-to-hex (values)
"Convert list of rgb color VALUES to a hex string, #XXXXXXXXXXXX.
Each X in the string is a hexadecimal digit.
Input VALUES is as for the output of `x-color-values'."
(concat "#" (hexrgb-int-to-hex (nth 0 values) 4) ; red
(hexrgb-int-to-hex (nth 1 values) 4) ; green
(hexrgb-int-to-hex (nth 2 values) 4))) ; blue
;;;###autoload
(defun hexrgb-hex-to-color-values (color)
"Convert hex COLOR to a list of rgb color values.
COLOR is a hex rgb color string, #XXXXXXXXXXXX
Each X in the string is a hexadecimal digit. There are 3N X's, N > 0.
The output list is as for `x-color-values'."
(let* ((hex-strgp (string-match
"^\\(#\\)?\\(\\([a-fA-F0-9][a-fA-F0-9][a-fA-F0-9]\\)+\\)$"
color))
(ndigits (/ (if (eq (match-beginning 1) (match-end 1))
(length color)
(1- (length color)))
3))
red green blue)
(unless hex-strgp (error "Invalid RGB color string: %s" color))
(setq color (substring color (match-beginning 2) (match-end 2))
red (hexrgb-hex-to-int (substring color 0 ndigits))
green (hexrgb-hex-to-int (substring color ndigits (* 2 ndigits)))
blue (hexrgb-hex-to-int (substring color ndigits (* 3 ndigits))))
(list red green blue)))
;;;###autoload
(defun hexrgb-increment-red (hex nb-digits increment &optional wrap-p)
"Increment red value of rgb string HEX by INCREMENT.
String HEX starts with \"#\". Each color is NB-DIGITS hex digits long.
If optional arg WRAP-P is non-nil, then the result wraps around zero.
For example, incrementing \"#FFFFFFFFF\" by 1 will cause it to wrap
around to \"#000000000\"."
(concat "#"
(hexrgb-increment-hex (substring hex 1 (1+ nb-digits)) increment nb-digits wrap-p)
(substring hex (1+ nb-digits) (1+ (* nb-digits 2)))
(substring hex (1+ (* nb-digits 2)))))
;;;###autoload
(defun hexrgb-increment-green (hex nb-digits increment &optional wrap-p)
"Increment green value of rgb string HEX by INCREMENT.
String HEX starts with \"#\". Each color is NB-DIGITS hex digits long.
For example, incrementing \"#FFFFFFFFF\" by 1 will cause it to wrap
around to \"#000000000\"."
(concat
"#" (substring hex 1 (1+ nb-digits))
(hexrgb-increment-hex (substring hex (1+ nb-digits) (1+ (* nb-digits 2)))
increment
nb-digits
wrap-p)
(substring hex (1+ (* nb-digits 2)))))
;;;###autoload
(defun hexrgb-increment-blue (hex nb-digits increment &optional wrap-p)
"Increment blue value of rgb string HEX by INCREMENT.
String HEX starts with \"#\". Each color is NB-DIGITS hex digits long.
For example, incrementing \"#FFFFFFFFF\" by 1 will cause it to wrap
around to \"#000000000\"."
(concat "#" (substring hex 1 (1+ (* nb-digits 2)))
(hexrgb-increment-hex (substring hex (1+ (* nb-digits 2)))
increment
nb-digits
wrap-p)))
;;;###autoload
(defun hexrgb-increment-equal-rgb (hex nb-digits increment &optional wrap-p)
"Increment each color value (r,g,b) of rgb string HEX by INCREMENT.
String HEX starts with \"#\". Each color is NB-DIGITS hex digits long.
For example, incrementing \"#FFFFFFFFF\" by 1 will cause it to wrap
around to \"#000000000\"."
(concat
"#" (hexrgb-increment-hex (substring hex 1 (1+ nb-digits)) increment nb-digits wrap-p)
(hexrgb-increment-hex (substring hex (1+ nb-digits) (1+ (* nb-digits 2)))
increment
nb-digits
wrap-p)
(hexrgb-increment-hex (substring hex (1+ (* nb-digits 2))) increment nb-digits wrap-p)))
;;;###autoload
(defun hexrgb-increment-hex (hex increment nb-digits &optional wrap-p)
"Increment HEX number (a string NB-DIGITS long) by INCREMENT.
For example, incrementing \"FFFFFFFFF\" by 1 will cause it to wrap
around to \"000000000\"."
(let* ((int (hexrgb-hex-to-int hex))
(new-int (+ increment int)))
(if (or wrap-p
(and (>= int 0) ; Not too large for the machine.
(>= new-int 0) ; For the case where increment < 0.
(<= (length (format (concat "%X") new-int)) nb-digits))) ; Not too long.
(hexrgb-int-to-hex new-int nb-digits) ; Use incremented number.
hex))) ; Don't increment.
;;;###autoload
(defun hexrgb-hex-to-int (hex)
"Convert HEX string argument to an integer.
The characters of HEX must be hex characters."
(let* ((factor 1)
(len (length hex))
(indx (1- len))
(int 0))
(while (>= indx 0)
(setq int (+ int (* factor (hexrgb-hex-char-to-integer (aref hex indx))))
indx (1- indx)
factor (* 16 factor)))
int))
;; From `hexl.el'. This is the same as `hexl-hex-char-to-integer' defined there.
;;;###autoload
(defun hexrgb-hex-char-to-integer (character)
"Take a CHARACTER and return its value as if it were a hex digit."
(if (and (>= character ?0) (<= character ?9))
(- character ?0)
(let ((ch (logior character 32)))
(if (and (>= ch ?a) (<= ch ?f))
(- ch (- ?a 10))
(error "Invalid hex digit `%c'" ch)))))
;; Originally, I used the code from `int-to-hex-string' in `float.el'.
;; This version is thanks to Juri Linkov <juri@jurta.org>.
;;
;;;###autoload
(defun hexrgb-int-to-hex (int &optional nb-digits)
"Convert integer argument INT to a #XXXXXXXXXXXX format hex string.
Each X in the output string is a hexadecimal digit.
NB-DIGITS is the number of hex digits. If INT is too large to be
represented with NB-DIGITS, then the result is truncated from the
left. So, for example, INT=256 and NB-DIGITS=2 returns \"00\", since
the hex equivalent of 256 decimal is 100, which is more than 2 digits."
(setq nb-digits (or nb-digits 4))
(substring (format (concat "%0" (int-to-string nb-digits) "X") int) (- nb-digits)))
;; Inspired by Elisp Info manual, node "Comparison of Numbers".
;;;###autoload
(defun hexrgb-approx-equal (x y &optional rfuzz afuzz)
"Return non-nil if numbers X and Y are approximately equal.
RFUZZ is a relative fuzz factor. AFUZZ is an absolute fuzz factor.
RFUZZ defaults to 1.0e-8. AFUZZ defaults to (/ RFUZZ 10).
RFUZZ and AFUZZ are converted to their absolute values.
The algorithm is:
(< (abs (- X Y)) (+ AFUZZ (* RFUZZ (+ (abs X) (abs Y)))))."
(setq rfuzz (or rfuzz 1.0e-8)
rfuzz (abs rfuzz)
afuzz (or afuzz (/ rfuzz 10))
afuzz (abs afuzz))
(< (abs (- x y)) (+ afuzz (* rfuzz (+ (abs x) (abs y))))))
;;;###autoload
(defun hexrgb-color-value-to-float (n)
"Return the floating-point equivalent of color value N.
N must be an integer between 0 and 65535, or else an error is raised."
(unless (and (wholenump n) (<= n 65535))
(error "Not a whole number less than 65536"))
(/ (float n) 65535.0))
;;;###autoload
(defun hexrgb-float-to-color-value (x)
"Return the color value equivalent of floating-point number X.
X must be between 0.0 and 1.0, or else an error is raised."
(unless (and (numberp x) (<= 0.0 x) (<= x 1.0))
(error "Not a floating-point number between 0.0 and 1.0"))
(floor (* x 65535.0)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;
(provide 'hexrgb)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; hexrgb.el ends here

View File

@ -1,125 +0,0 @@
;; jabber-feature-neg.el - Feature Negotiation by JEP-0020
;; Copyright (C) 2002, 2003, 2004 - tom berger - object@intelectronica.net
;; Copyright (C) 2003, 2004 - Magnus Henoch - mange@freemail.hu
;; This file is a part of jabber.el.
;; 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
(require 'jabber-disco)
(require 'cl)
(jabber-disco-advertise-feature "http://jabber.org/protocol/feature-neg")
(defun jabber-fn-parse (xml-data type)
"Parse a Feature Negotiation request, return alist representation.
XML-DATA should have one child element, <x/>, in the jabber:x:data
namespace.
TYPE is either 'request or 'response.
Returned alist has field name as key, and value is a list of offered
alternatives."
(let ((x (car (jabber-xml-get-children xml-data 'x))))
(unless (and x
(string= (jabber-xml-get-attribute x 'xmlns) "jabber:x:data"))
(jabber-signal-error "modify" 'bad-request "Malformed Feature Negotiation"))
(let (alist
(fields (jabber-xml-get-children x 'field)))
(dolist (field fields)
(let ((var (jabber-xml-get-attribute field 'var))
(value (car (jabber-xml-get-children field 'value)))
(options (jabber-xml-get-children field 'option)))
(setq alist (cons
(cons var
(cond
((eq type 'request)
(mapcar #'(lambda (option)
(car (jabber-xml-node-children
(car (jabber-xml-get-children
option 'value)))))
options))
((eq type 'response)
(jabber-xml-node-children value))
(t
(error "Incorrect Feature Negotiation type: %s" type))))
alist))))
;; return alist
alist)))
(defun jabber-fn-encode (alist type)
"Transform a feature alist into an <x/> node int the jabber:x:data namespace.
Note that this is not the reverse of `jabber-fn-parse'.
TYPE is either 'request or 'response."
(let ((requestp (eq type 'request)))
`(x ((xmlns . "jabber:x:data")
(type . ,(if requestp "form" "submit")))
,@(mapcar #'(lambda (field)
`(field
((type . "list-single")
(var . ,(car field)))
,@(if requestp
(mapcar
#'(lambda (option)
`(option nil (value nil ,option)))
(cdr field))
(list `(value nil ,(cadr field))))))
alist))))
(defun jabber-fn-intersection (mine theirs)
"Find values acceptable to both parties.
MINE and THEIRS are alists, as returned by `jabber-fn-parse'.
An alist is returned, where the keys are the negotiated variables,
and the values are lists containing the preferred option. If
negotiation is impossible, an error is signalled. The errors are as
specified in JEP-0020, and not necessarily the ones of higher-level
protocols."
(let ((vars (mapcar #'car mine))
(their-vars (mapcar #'car theirs)))
;; are the same variables being negotiated?
(sort vars 'string-lessp)
(sort their-vars 'string-lessp)
(let ((mine-but-not-theirs (set-difference vars their-vars :test 'string=))
(theirs-but-not-mine (set-difference their-vars vars :test 'string=)))
(when mine-but-not-theirs
(jabber-signal-error "modify" 'not-acceptable (car mine-but-not-theirs)))
(when theirs-but-not-mine
(jabber-signal-error "cancel" 'feature-not-implemented (car theirs-but-not-mine))))
(let (alist)
(dolist (var vars)
(let ((my-options (cdr (assoc var mine)))
(their-options (cdr (assoc var theirs))))
(let ((common-options (intersection my-options their-options :test 'string=)))
(if common-options
;; we have a match; but which one to use?
;; the first one will probably work
(setq alist
(cons (list var (car common-options))
alist))
;; no match
(jabber-signal-error "modify" 'not-acceptable var)))))
alist)))
(provide 'jabber-feature-neg)
;;; arch-tag: 65b2cdcc-7a5f-476b-a613-84ec8e590186

View File

@ -1,35 +0,0 @@
;;; jabber-festival.el --- Festival alert hooks
;; Copyright (C) 2005 Magnus Henoch
;; This file is a part of jabber.el.
;; 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, 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 GNU Emacs; see the file COPYING. If not, write to
;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
;; Boston, MA 02111-1307, USA.
(eval-when-compile (require 'jabber-alert))
(condition-case e
(progn
;; Most people don't have Festival, so this will often fail
(require 'festival)
(define-jabber-alert festival "Voice messages through Festival"
(lambda (text &optional title) (festival-say-string (or title text)))))
(error nil))
(provide 'jabber-festival)
;; arch-tag: 8922D096-5D07-11D9-B4C2-000A95C2FCD0

View File

@ -1,68 +0,0 @@
;; jabber-ft-client.el - send file transfer requests, by JEP-0096
;; Copyright (C) 2004 - Magnus Henoch - mange@freemail.hu
;; This file is a part of jabber.el.
;; 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
(eval-when-compile (require 'cl))
(require 'jabber-si-client)
(require 'jabber-util)
(require 'jabber-ft-common)
(defun jabber-ft-send (jc jid filename desc)
"Attempt to send FILENAME to JID."
(interactive (list (jabber-read-account)
(jabber-read-jid-completing "Send file to: " nil nil nil 'full t)
(read-file-name "Send which file: " nil nil t)
(jabber-read-with-input-method "Description (optional): ")))
(if (zerop (length desc)) (setq desc nil))
(setq filename (expand-file-name filename))
(access-file filename "Couldn't open file")
(let* ((attributes (file-attributes filename))
(size (nth 7 attributes))
(date (nth 5 attributes))
(hash (jabber-ft-get-md5 filename)))
(jabber-si-initiate jc jid "http://jabber.org/protocol/si/profile/file-transfer"
`(file ((xmlns . "http://jabber.org/protocol/si/profile/file-transfer")
(name . ,(file-name-nondirectory filename))
(size . ,size)
(date . ,(jabber-encode-time date))
,@(when hash
(list (cons 'hash hash))))
(desc () ,desc))
(lexical-let ((filename filename))
(lambda (jc jid sid send-data-function)
(jabber-ft-do-send
jid sid send-data-function filename))))))
(defun jabber-ft-do-send (jid sid send-data-function filename)
(if (stringp send-data-function)
(message "File sending failed: %s" send-data-function)
(with-temp-buffer
(insert-file-contents-literally filename)
;; Ever heard of buffering?
(funcall send-data-function (buffer-string))
(message "File transfer completed")))
;; File transfer is monodirectional, so ignore received data.
#'ignore)
(provide 'jabber-ft-client)
;;; arch-tag: fba686d5-37b5-4165-86c5-49b76fa0ea6e

View File

@ -1,46 +0,0 @@
;;; jabber-ft-common.el --- Common functions for sending and receiving files (JEP-0096)
;; Copyright (C) 2006, 2008 Magnus Henoch
;; Author: Magnus Henoch <mange@freemail.hu>
;; This file 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.
;; This file 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 GNU Emacs; see the file COPYING. If not, write to
;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
;; Boston, MA 02110-1301, USA.
(defcustom jabber-ft-md5sum-program (or (when (executable-find "md5")
(list (executable-find "md5") "-n"))
(when (executable-find "md5sum")
(list (executable-find "md5sum"))))
"The program to use to calculate MD5 sums of files.
The first item should be the name of the program, and the remaing
items the arguments. The file name is appended as the last
argument."
:type '(repeat string)
:group 'jabber)
(defun jabber-ft-get-md5 (file-name)
"Get MD5 sum of FILE-NAME, and return as hex string.
Return nil if no MD5 summing program is available."
(when jabber-ft-md5sum-program
(with-temp-buffer
(apply 'call-process (car jabber-ft-md5sum-program) nil t nil
(append (cdr jabber-ft-md5sum-program) (list file-name)))
;; Output is "hexsum filename"
(goto-char (point-min))
(forward-word 1)
(buffer-substring (point-min) (point)))))
(provide 'jabber-ft-common)
;; arch-tag: 1ce4cce0-8360-11da-a5ba-000a95c2fcd0

View File

@ -1,131 +0,0 @@
;; jabber-ft-server.el - handle incoming file transfers, by JEP-0096
;; Copyright (C) 2003, 2004, 2007 - Magnus Henoch - mange@freemail.hu
;; Copyright (C) 2002, 2003, 2004 - tom berger - object@intelectronica.net
;; This file is a part of jabber.el.
;; 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
(require 'jabber-si-server)
(require 'jabber-util)
(defvar jabber-ft-sessions nil
"Alist, where keys are (sid jid), and values are buffers of the files.")
(defvar jabber-ft-size nil
"Size of the file that is being downloaded")
(defvar jabber-ft-md5-hash nil
"MD5 hash of the file that is being downloaded")
(jabber-disco-advertise-feature "http://jabber.org/protocol/si/profile/file-transfer")
(add-to-list 'jabber-si-profiles
(list "http://jabber.org/protocol/si/profile/file-transfer"
'jabber-ft-accept
'jabber-ft-server-connected))
(defun jabber-ft-accept (jc xml-data)
"Receive IQ stanza containing file transfer request, ask user"
(let* ((from (jabber-xml-get-attribute xml-data 'from))
(query (jabber-iq-query xml-data))
(si-id (jabber-xml-get-attribute query 'id))
;; TODO: check namespace
(file (car (jabber-xml-get-children query 'file)))
(name (jabber-xml-get-attribute file 'name))
(size (jabber-xml-get-attribute file 'size))
(date (jabber-xml-get-attribute file 'date))
(md5-hash (jabber-xml-get-attribute file 'hash))
(desc (car (jabber-xml-node-children
(car (jabber-xml-get-children file 'desc)))))
(range (car (jabber-xml-get-children file 'range))))
(unless (and name size)
;; both name and size must be present
(jabber-signal-error "modify" 'bad-request))
(let ((question (format
"%s is sending you the file %s (%s bytes).%s Accept? "
(jabber-jid-displayname from)
name
size
(if (not (zerop (length desc)))
(concat " Description: '" desc "'")
""))))
(unless (yes-or-no-p question)
(jabber-signal-error "cancel" 'forbidden)))
;; default is to save with given name, in current directory.
;; maybe that's bad; maybe should be customizable.
(let* ((file-name (read-file-name "Download to: " nil nil nil name))
(buffer (create-file-buffer file-name)))
(message "Starting download of %s..." (file-name-nondirectory file-name))
(with-current-buffer buffer
(kill-all-local-variables)
(setq buffer-file-coding-system 'binary)
;; For Emacs, switch buffer to unibyte _before_ anything goes into it,
;; otherwise binary files are corrupted. For XEmacs, it isn't needed,
;; and it also doesn't have set-buffer-multibyte.
(if (fboundp 'set-buffer-multibyte)
(set-buffer-multibyte nil))
(set-visited-file-name file-name t)
(set (make-local-variable 'jabber-ft-size)
(string-to-number size))
(set (make-local-variable 'jabber-ft-md5-hash)
md5-hash))
(add-to-list 'jabber-ft-sessions
(cons (list si-id from) buffer)))
;; to support range, return something sensible here
nil))
(defun jabber-ft-server-connected (jc jid sid send-data-function)
;; We don't really care about the send-data-function. But if it's
;; a string, it means that we have no connection.
(if (stringp send-data-function)
(message "File receiving failed: %s" send-data-function)
;; On success, we just return our data receiving function.
'jabber-ft-data))
(defun jabber-ft-data (jc jid sid data)
"Receive chunk of transferred file."
(let ((buffer (cdr (assoc (list sid jid) jabber-ft-sessions))))
(with-current-buffer buffer
;; If data is nil, there is no more data.
;; But maybe the remote entity doesn't close the stream -
;; then we have to keep track of file size to know when to stop.
;; Return value is whether to keep connection open.
(when data
(insert data))
(if (and data (< (buffer-size) jabber-ft-size))
t
(basic-save-buffer)
(if (and jabber-ft-md5-hash
(let ((file-hash (jabber-ft-get-md5 buffer-file-name)))
(and file-hash
(not (string= file-hash jabber-ft-md5-hash)))))
;; hash mismatch!
(progn
(message "%s downloaded - CHECKSUM MISMATCH!"
(file-name-nondirectory buffer-file-name))
(sleep-for 5))
;; all is fine
(message "%s downloaded" (file-name-nondirectory buffer-file-name)))
(kill-buffer buffer)
nil))))
(provide 'jabber-ft-server)
;;; arch-tag: 334adcff-6210-496e-8382-8f49ae0248a1

View File

@ -1,98 +0,0 @@
;;; jabber-gmail.el --- Gmail notifications via emacs-jabber
;; Copyright (C) 2008 Magnus Henoch <mange@freemail.hu>
;; Copyright (C) 2007 Valery V. Vorotyntsev <valery.vv@gmail.com>
;; 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., 51 Franklin Street, Fifth Floor,
;; Boston, MA 02110-1301, USA.
;;; Usage:
;; Add the following line to your ~/.emacs:
;;
;; (require 'jabber-gmail)
;;
;; If you prefer on demand loading
;; [http://a-nickels-worth.blogspot.com/2007/11/effective-emacs.html]:
;;
;; (autoload 'jabber-gmail-query "jabber-gmail")
;; (autoload 'jabber-gmail-subscribe "jabber-gmail")
;; (add-hook 'jabber-post-connect-hook 'jabber-gmail-subscribe)
;;
;; You may wish to bind a shortcut for `jabber-gmail-query'
;;
;; (global-set-key (kbd "<f9> g") 'jabber-gmail-query)
;;
;; or to customize `jabber-gmail-dothreads'
;;
;; (defun jabber-gmail-dothreads (ts)
;; (let ((msg (format "%d new messages in gmail inbox" (length ts))))
;; (message msg)
;; (jabber-screen-message msg)))
;;;###autoload
(defun jabber-gmail-subscribe (jc)
"Subscribe to gmail notifications.
See http://code.google.com/apis/talk/jep_extensions/usersettings.html#4"
(interactive (list (jabber-read-account)))
(jabber-send-iq jc (jabber-connection-bare-jid jc) "set"
'(usersetting ((xmlns . "google:setting"))
(mailnotifications ((value . "true"))))
#'jabber-report-success "Gmail subscription"
#'jabber-process-data "Gmail subscription")
;; Looks like "one shot" request is still needed to activate
;; notifications machinery.
(jabber-gmail-query jc))
(add-to-list 'jabber-iq-set-xmlns-alist
(cons "google:mail:notify" #'jabber-gmail-process-new-mail))
(defun jabber-gmail-process-new-mail (jc xml-sexp)
"Process new gmail notification.
See http://code.google.com/apis/talk/jep_extensions/gmail.html#notifications"
(let ((from (jabber-xml-get-attribute xml-sexp 'from))
(id (jabber-xml-get-attribute xml-sexp 'id)))
;; respond to server
(jabber-send-iq jc from "result" nil
nil nil nil nil
id))
(jabber-gmail-query jc))
;;;###autoload
(defun jabber-gmail-query (jc)
"Request mail information from the Google Talk server (a.k.a. one shot query).
See http://code.google.com/apis/talk/jep_extensions/gmail.html#requestmail"
(interactive (list (jabber-read-account)))
(jabber-send-iq jc (jabber-connection-bare-jid jc) "get"
'(query ((xmlns . "google:mail:notify")))
#'jabber-gmail-process-mailbox nil
#'jabber-process-data "Gmail query" "gmail-query"))
(defun jabber-gmail-process-mailbox (jc xml-sexp &rest ignore)
"Process gmail query response.
See http://code.google.com/apis/talk/jep_extensions/gmail.html#response"
(let ((ts (jabber-xml-node-children
(car (jabber-xml-get-children xml-sexp 'mailbox)))))
(when ts (jabber-gmail-dothreads ts))))
(defun jabber-gmail-dothreads (threads)
"Process <mail-thread-info/> elements.
THREADS is a list of XML sexps, corresponding to <mail-thread-info/> elements.
See http://code.google.com/apis/talk/jep_extensions/gmail.html#response"
(message "%d new messages in gmail inbox" (length threads)))
(provide 'jabber-gmail)
;; arch-tag: 102bc8e4-e08f-11dc-ab66-000a95c2fcd0

View File

@ -1,337 +0,0 @@
;; jabber-history.el - recording message history
;; Copyright (C) 2004, 2007, 2008 - Magnus Henoch - mange@freemail.hu
;; Copyright (C) 2004 - Mathias Dahl
;; This file is a part of jabber.el.
;; 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
;;; Log format:
;; Each message is on one separate line, represented as a vector with
;; five elements. The first element is time encoded according to
;; JEP-0082. The second element is direction, "in" or "out".
;; The third element is the sender, "me" or a JID. The fourth
;; element is the recipient. The fifth element is the text
;; of the message.
;; FIXME: when rotation is enabled, jabber-history-query won't look
;; for older history files if the current history file doesn't contain
;; enough backlog entries.
(require 'jabber-core)
(require 'jabber-util)
(defgroup jabber-history nil "Customization options for Emacs
Jabber history files."
:group 'jabber)
(defcustom jabber-history-enabled nil
"Non-nil means message logging is enabled."
:type 'boolean
:group 'jabber-history)
(defcustom jabber-history-muc-enabled nil
"Non-nil means MUC logging is enabled.
Default is nil, cause MUC logging may be i/o-intensive."
:type 'boolean
:group 'jabber-history)
(defcustom jabber-history-dir
(locate-user-emacs-file "jabber-history" ".emacs-jabber")
"Base directory where per-contact history files are stored.
Used only when `jabber-use-global-history' is nil."
:type 'directory
:group 'jabber-history)
(defcustom jabber-global-history-filename
(locate-user-emacs-file "jabber-global-message-log" ".jabber_global_message_log")
"Global file where all messages are logged.
Used when `jabber-use-global-history' is non-nil."
:type 'file
:group 'jabber-history)
(defcustom jabber-use-global-history
;; Using a global history file by default was a bad idea. Let's
;; default to per-user files unless the global history file already
;; exists, to avoid breaking existing installations.
(file-exists-p jabber-global-history-filename)
"Whether to use a global file for message history.
If non-nil, `jabber-global-history-filename' is used, otherwise,
messages are stored in per-user files under the
`jabber-history-dir' directory."
:type 'boolean
:group 'jabber-history)
(defcustom jabber-history-enable-rotation nil
"Whether history files should be renamed when reach
`jabber-history-size-limit' kilobytes. If nil, history files
will grow indefinitely, otherwise they'll be renamed to
<history-file>-<number>, where <number> is 1 or the smallest
number after the last rotation."
:type 'boolean
:group 'jabber-history)
(defcustom jabber-history-size-limit 1024
"Maximum history file size in kilobytes.
When history file reaches this limit, it is renamed to
<history-file>-<number>, where <number> is 1 or the smallest
number after the last rotation."
:type 'integer
:group 'jabber-history)
(defvar jabber-history-inhibit-received-message-functions nil
"Functions determining whether to log an incoming message stanza.
The functions in this list are called with two arguments,
the connection and the full message stanza.
If any of the functions returns non-nil, the stanza is not logged
in the message history.")
(defun jabber-rotate-history-p (history-file)
"Return true if HISTORY-FILE should be rotated."
(when (and jabber-history-enable-rotation
(file-exists-p history-file))
(> (/ (nth 7 (file-attributes history-file)) 1024)
jabber-history-size-limit)))
(defun jabber-history-rotate (history-file &optional try)
"Rename HISTORY-FILE to HISTORY-FILE-TRY."
(let ((suffix (number-to-string (or try 1))))
(if (file-exists-p (concat history-file "-" suffix))
(jabber-history-rotate history-file (if try (1+ try) 1))
(rename-file history-file (concat history-file "-" suffix)))))
(add-to-list 'jabber-message-chain 'jabber-message-history)
(defun jabber-message-history (jc xml-data)
"Log message to log file."
(when (and (not jabber-use-global-history)
(not (file-directory-p jabber-history-dir)))
(make-directory jabber-history-dir))
(let ((is-muc (jabber-muc-message-p xml-data)))
(when (and jabber-history-enabled
(or
(not is-muc) ;chat message or private MUC message
(and jabber-history-muc-enabled is-muc))) ;muc message and muc logging active
(unless (run-hook-with-args-until-success
'jabber-history-inhibit-received-message-functions
jc xml-data)
(let ((from (jabber-xml-get-attribute xml-data 'from))
(text (car (jabber-xml-node-children
(car (jabber-xml-get-children xml-data 'body)))))
(timestamp (jabber-message-timestamp xml-data)))
(when (and from text)
(jabber-history-log-message "in" from nil text timestamp)))))))
(add-hook 'jabber-chat-send-hooks 'jabber-history-send-hook)
(defun jabber-history-send-hook (body id)
"Log outgoing message to log file."
(when (and (not jabber-use-global-history)
(not (file-directory-p jabber-history-dir)))
(make-directory jabber-history-dir))
;; This function is called from a chat buffer, so jabber-chatting-with
;; contains the desired value.
(if jabber-history-enabled
(jabber-history-log-message "out" nil jabber-chatting-with body (current-time))))
(defun jabber-history-filename (contact)
"Return a history filename for CONTACT if the per-user file
loggin strategy is used or the global history filename."
(if jabber-use-global-history
jabber-global-history-filename
;; jabber-jid-symbol is the best canonicalization we have.
(concat jabber-history-dir
"/" (symbol-name (jabber-jid-symbol contact)))))
(defun jabber-history-log-message (direction from to body timestamp)
"Log a message"
(with-temp-buffer
;; Remove properties
(set-text-properties 0 (length body) nil body)
;; Encode text as Lisp string - get decoding for free
(setq body (prin1-to-string body))
;; Encode LF and CR
(while (string-match "\n" body)
(setq body (replace-match "\\n" nil t body nil)))
(while (string-match "\r" body)
(setq body (replace-match "\\r" nil t body nil)))
(insert (format "[\"%s\" \"%s\" %s %s %s]\n"
(jabber-encode-time (or timestamp (current-time)))
(or direction
"in")
(or (when from
(prin1-to-string from))
"\"me\"")
(or (when to
(prin1-to-string to))
"\"me\"")
body))
(let ((coding-system-for-write 'utf-8)
(history-file (jabber-history-filename (or from to))))
(when (and (not jabber-use-global-history)
(not (file-directory-p jabber-history-dir)))
(make-directory jabber-history-dir))
(when (jabber-rotate-history-p history-file)
(jabber-history-rotate history-file))
(condition-case e
(write-region (point-min) (point-max) history-file t 'quiet)
(error
(message "Unable to write history: %s" (error-message-string e)))))))
(defun jabber-history-query (start-time
end-time
number
direction
jid-regexp
history-file)
"Return a list of vectors, one for each message matching the criteria.
START-TIME and END-TIME are floats as obtained from `float-time'.
Either or both may be nil, meaning no restriction.
NUMBER is the maximum number of messages to return, or t for
unlimited.
DIRECTION is either \"in\" or \"out\", or t for no limit on direction.
JID-REGEXP is a regexp which must match the JID.
HISTORY-FILE is the file in which to search.
Currently jabber-history-query performs a linear search from the end
of the log file."
(when (file-readable-p history-file)
(with-temp-buffer
(let ((coding-system-for-read 'utf-8))
(if jabber-use-global-history
(insert-file-contents history-file)
(let* ((lines-collected nil)
(matched-files
(directory-files jabber-history-dir t
(concat "^"
(regexp-quote (file-name-nondirectory
history-file)))))
(matched-files
(cons (car matched-files)
(sort (cdr matched-files) 'string>-numerical))))
(while (not lines-collected)
(if (null matched-files)
(setq lines-collected t)
(let ((file (pop matched-files)))
(progn
(insert-file-contents file)
(when (numberp number)
(if (>= (count-lines (point-min) (point-max)) number)
(setq lines-collected t))))))))))
(let (collected current-line)
(goto-char (point-max))
(catch 'beginning-of-file
(while (progn
(backward-sexp)
(setq current-line (car (read-from-string
(buffer-substring
(point)
(save-excursion
(forward-sexp)
(point))))))
(and (or (null start-time)
(> (jabber-float-time (jabber-parse-time
(aref current-line 0)))
start-time))
(or (eq number t)
(< (length collected) number))))
(if (and (or (eq direction t)
(string= direction (aref current-line 1)))
(or (null end-time)
(> end-time (jabber-float-time (jabber-parse-time
(aref current-line 0)))))
(string-match
jid-regexp
(car
(remove "me"
(list (aref current-line 2)
(aref current-line 3))))))
(push current-line collected))
(when (bobp)
(throw 'beginning-of-file nil))))
collected))))
(defcustom jabber-backlog-days 3.0
"Age limit on messages in chat buffer backlog, in days"
:group 'jabber
:type '(choice (number :tag "Number of days")
(const :tag "No limit" nil)))
(defcustom jabber-backlog-number 10
"Maximum number of messages in chat buffer backlog"
:group 'jabber
:type 'integer)
(defun jabber-history-backlog (jid &optional before)
"Fetch context from previous chats with JID.
Return a list of history entries (vectors), limited by
`jabber-backlog-days' and `jabber-backlog-number'.
If BEFORE is non-nil, it should be a float-time after which
no entries will be fetched. `jabber-backlog-days' still
applies, though."
(jabber-history-query
(and jabber-backlog-days
(- (jabber-float-time) (* jabber-backlog-days 86400.0)))
before
jabber-backlog-number
t ; both incoming and outgoing
(concat "^" (regexp-quote (jabber-jid-user jid)) "\\(/.*\\)?$")
(jabber-history-filename jid)))
(defun jabber-history-move-to-per-user ()
"Migrate global history to per-user files."
(interactive)
(when (file-directory-p jabber-history-dir)
(error "Per-user history directory already exists"))
(make-directory jabber-history-dir)
(let ((jabber-use-global-history nil))
(with-temp-buffer
(let ((coding-system-for-read 'utf-8))
(insert-file-contents jabber-global-history-filename))
(let ((progress-reporter
(when (fboundp 'make-progress-reporter)
(make-progress-reporter "Migrating history..."
(point-min) (point-max))))
;;(file-table (make-hash-table :test 'equal))
;; Keep track of blocks of entries pertaining to the same JID.
current-jid jid-start)
(while (not (eobp))
(let* ((start (point))
(end (progn (forward-line) (point)))
(line (buffer-substring start end))
(parsed (car (read-from-string line)))
(jid (if (string= (aref parsed 2) "me")
(aref parsed 3)
(aref parsed 2))))
;; Whenever there is a change in JID...
(when (not (equal jid current-jid))
(when current-jid
;; ...save data for previous JID...
(let ((history-file (jabber-history-filename current-jid)))
(write-region jid-start start history-file t 'quiet)))
;; ...and switch to new JID.
(setq current-jid jid)
(setq jid-start start))
(when (fboundp 'progress-reporter-update)
(progress-reporter-update progress-reporter (point)))))
;; Finally, save the last block, if any.
(when current-jid
(let ((history-file (jabber-history-filename current-jid)))
(write-region jid-start (point-max) history-file t 'quiet))))))
(message "Done. Please change `jabber-use-global-history' now."))
(provide 'jabber-history)
;; arch-tag: 0AA0C235-3FC0-11D9-9FE7-000A95C2FCD0

View File

@ -1,213 +0,0 @@
;; jabber-iq.el - infoquery functions
;; Copyright (C) 2003, 2004, 2007, 2008 - Magnus Henoch - mange@freemail.hu
;; Copyright (C) 2002, 2003, 2004 - tom berger - object@intelectronica.net
;; This file is a part of jabber.el.
;; 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
(require 'jabber-core)
(require 'jabber-util)
(require 'jabber-keymap)
(defvar *jabber-open-info-queries* nil
"an alist of open query id and their callback functions")
(defvar jabber-iq-get-xmlns-alist nil
"Mapping from XML namespace to handler for IQ GET requests.")
(defvar jabber-iq-set-xmlns-alist nil
"Mapping from XML namespace to handler for IQ SET requests.")
(defvar jabber-browse-mode-map
(let ((map (make-sparse-keymap)))
(set-keymap-parent map jabber-common-keymap)
(define-key map [mouse-2] 'jabber-popup-combined-menu)
map))
(defcustom jabber-browse-mode-hook nil
"Hook run when entering Browse mode."
:group 'jabber
:type 'hook)
(defgroup jabber-browse nil "browse display options"
:group 'jabber)
(defcustom jabber-browse-buffer-format "*-jabber-browse:-%n-*"
"The format specification for the name of browse buffers.
These fields are available at this moment:
%n JID to browse"
:type 'string
:group 'jabber-browse)
(defun jabber-browse-mode ()
"\\{jabber-browse-mode-map}"
(kill-all-local-variables)
(setq major-mode 'jabber-browse-mode
mode-name "jabber-browse")
(use-local-map jabber-browse-mode-map)
(setq buffer-read-only t)
(if (fboundp 'run-mode-hooks)
(run-mode-hooks 'jabber-browse-mode-hook)
(run-hooks 'jabber-browse-mode-hook)))
(put 'jabber-browse-mode 'mode-class 'special)
(add-to-list 'jabber-iq-chain 'jabber-process-iq)
(defun jabber-process-iq (jc xml-data)
"process an incoming iq stanza"
(let* ((id (jabber-xml-get-attribute xml-data 'id))
(type (jabber-xml-get-attribute xml-data 'type))
(from (jabber-xml-get-attribute xml-data 'from))
(query (jabber-iq-query xml-data))
(callback (assoc id *jabber-open-info-queries*)))
(cond
;; if type is "result" or "error", this is a response to a query we sent.
((or (string= type "result")
(string= type "error"))
(let ((callback-cons (nth (cdr (assoc type '(("result" . 0)
("error" . 1)))) (cdr callback))))
(if (consp callback-cons)
(funcall (car callback-cons) jc xml-data (cdr callback-cons))))
(setq *jabber-open-info-queries* (delq callback *jabber-open-info-queries*)))
;; if type is "get" or "set", correct action depends on namespace of request.
((and (listp query)
(or (string= type "get")
(string= type "set")))
(let* ((which-alist (eval (cdr (assoc type
(list
(cons "get" 'jabber-iq-get-xmlns-alist)
(cons "set" 'jabber-iq-set-xmlns-alist))))))
(handler (cdr (assoc (jabber-xml-get-attribute query 'xmlns) which-alist))))
(if handler
(condition-case error-var
(funcall handler jc xml-data)
(jabber-error
(apply 'jabber-send-iq-error jc from id query (cdr error-var)))
(error (jabber-send-iq-error jc from id query "wait" 'internal-server-error (error-message-string error-var))))
(jabber-send-iq-error jc from id query "cancel" 'feature-not-implemented)))))))
(defun jabber-send-iq (jc to type query success-callback success-closure-data
error-callback error-closure-data &optional result-id)
"Send an iq stanza to the specified entity, and optionally set up a callback.
JC is the Jabber connection.
TO is the addressee.
TYPE is one of \"get\", \"set\", \"result\" or \"error\".
QUERY is a list containing the child of the iq node in the format `jabber-sexp2xml'
accepts.
SUCCESS-CALLBACK is the function to be called when a successful result arrives.
SUCCESS-CLOSURE-DATA is an extra argument to SUCCESS-CALLBACK.
ERROR-CALLBACK is the function to be called when an error arrives.
ERROR-CLOSURE-DATA is an extra argument to ERROR-CALLBACK.
RESULT-ID is the id to be used for a response to a received iq message.
`jabber-report-success' and `jabber-process-data' are common callbacks.
The callback functions are called like this:
\(funcall CALLBACK JC XML-DATA CLOSURE-DATA)
with XML-DATA being the IQ stanza received in response. "
(let ((id (or result-id (apply 'format "emacs-iq-%d.%d.%d" (current-time)))))
(if (or success-callback error-callback)
(setq *jabber-open-info-queries* (cons (list id
(cons success-callback success-closure-data)
(cons error-callback error-closure-data))
*jabber-open-info-queries*)))
(jabber-send-sexp jc
(list 'iq (append
(if to (list (cons 'to to)))
(list (cons 'type type))
(list (cons 'id id)))
query))))
(defun jabber-send-iq-error (jc to id original-query error-type condition
&optional text app-specific)
"Send an error iq stanza to the specified entity in response to a
previously sent iq stanza.
TO is the addressee.
ID is the id of the iq stanza that caused the error.
ORIGINAL-QUERY is the original query, which should be included in the
error, or nil.
ERROR-TYPE is one of \"cancel\", \"continue\", \"modify\", \"auth\"
and \"wait\".
CONDITION is a symbol denoting a defined XMPP condition.
TEXT is a string to be sent in the error message, or nil for no text.
APP-SPECIFIC is a list of extra XML tags.
See section 9.3 of XMPP Core."
(jabber-send-sexp
jc
`(iq (,@(when to `((to . ,to)))
(type . "error")
(id . ,(or id "")))
,original-query
(error ((type . ,error-type))
(,condition ((xmlns . "urn:ietf:params:xml:ns:xmpp-stanzas")))
,(if text
`(text ((xmlns . "urn:ietf:params:xml:ns:xmpp-stanzas"))
,text))
,@app-specific))))
(defun jabber-process-data (jc xml-data closure-data)
"Process random results from various requests."
(let ((from (or (jabber-xml-get-attribute xml-data 'from) (plist-get (fsm-get-state-data jc) :server)))
(xmlns (jabber-iq-xmlns xml-data))
(type (jabber-xml-get-attribute xml-data 'type)))
(with-current-buffer (get-buffer-create (format-spec jabber-browse-buffer-format
(list (cons ?n from))))
(if (not (eq major-mode 'jabber-browse-mode))
(jabber-browse-mode))
(setq buffer-read-only nil)
(goto-char (point-max))
(insert (jabber-propertize from
'face 'jabber-title-large) "\n\n")
;; Put point at beginning of data
(save-excursion
;; If closure-data is a function, call it. If it is a string,
;; output it along with a description of the error. For other
;; values (e.g. nil), just dump the XML.
(cond
((functionp closure-data)
(funcall closure-data jc xml-data))
((stringp closure-data)
(insert closure-data ": " (jabber-parse-error (jabber-iq-error xml-data)) "\n\n"))
(t
(insert (format "%S\n\n" xml-data))))
(dolist (hook '(jabber-info-message-hooks jabber-alert-info-message-hooks))
(run-hook-with-args hook 'browse (current-buffer) (funcall jabber-alert-info-message-function 'browse (current-buffer))))))))
(defun jabber-silent-process-data (jc xml-data closure-data)
"Process random results from various requests to only alert hooks."
(let ((text (cond
((functionp closure-data)
(funcall closure-data jc xml-data))
((stringp closure-data)
(concat closure-data ": " (jabber-parse-error (jabber-iq-error xml-data))))
(t
(format "%S" xml-data)))))
(dolist (hook '(jabber-info-message-hooks jabber-alert-info-message-hooks))
(run-hook-with-args hook 'browse (current-buffer)
text))))
(provide 'jabber-iq)
;;; arch-tag: 5585dfa3-b59a-42ee-9292-803652c85e26

View File

@ -1,176 +0,0 @@
;; jabber-keepalive.el - try to detect lost connection
;; Copyright (C) 2004, 2008 - Magnus Henoch - mange@freemail.hu
;; Copyright (C) 2007 - Detlev Zundel - dzu@gnu.org
;; This file is a part of jabber.el.
;; 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
;;;; Keepalive - send something to the server and see if it answers
;;;
;;; These keepalive functions send a urn:xmpp:ping request to the
;;; server every X minutes, and considers the connection broken if
;;; they get no answer within Y seconds.
(require 'jabber-ping)
;;;###autoload
(defgroup jabber-keepalive nil
"Keepalive functions try to detect lost connection"
:group 'jabber)
(defcustom jabber-keepalive-interval 600
"Interval in seconds between connection checks."
:type 'integer
:group 'jabber-keepalive)
(defcustom jabber-keepalive-timeout 20
"Seconds to wait for response from server."
:type 'integer
:group 'jabber-keepalive)
(defvar jabber-keepalive-timer nil
"Timer object for keepalive function")
(defvar jabber-keepalive-timeout-timer nil
"Timer object for keepalive timeout function")
(defvar jabber-keepalive-pending nil
"List of outstanding keepalive connections")
(defvar jabber-keepalive-debug nil
"Log keepalive traffic when non-nil")
;;;###autoload
(defun jabber-keepalive-start (&optional jc)
"Activate keepalive.
That is, regularly send a ping request to the server, and
disconnect if it doesn't answer. See `jabber-keepalive-interval'
and `jabber-keepalive-timeout'.
The JC argument makes it possible to add this function to
`jabber-post-connect-hooks'; it is ignored. Keepalive is activated
for all accounts regardless of the argument."
(interactive)
(when jabber-keepalive-timer
(jabber-keepalive-stop))
(setq jabber-keepalive-timer
(run-with-timer 5
jabber-keepalive-interval
'jabber-keepalive-do))
(add-hook 'jabber-post-disconnect-hook 'jabber-keepalive-stop))
(defun jabber-keepalive-stop ()
"Deactivate keepalive"
(interactive)
(when jabber-keepalive-timer
(jabber-cancel-timer jabber-keepalive-timer)
(setq jabber-keepalive-timer nil)))
(defun jabber-keepalive-do ()
(when jabber-keepalive-debug
(message "%s: sending keepalive packet(s)" (current-time-string)))
(setq jabber-keepalive-timeout-timer
(run-with-timer jabber-keepalive-timeout
nil
'jabber-keepalive-timeout))
(setq jabber-keepalive-pending jabber-connections)
(dolist (c jabber-connections)
;; Whether we get an error or not is not interesting.
;; Getting a response at all is.
(jabber-ping-send c nil 'jabber-keepalive-got-response nil nil)))
(defun jabber-keepalive-got-response (jc &rest args)
(when jabber-keepalive-debug
(message "%s: got keepalive response from %s"
(current-time-string)
(plist-get (fsm-get-state-data jc) :server)))
(setq jabber-keepalive-pending (remq jc jabber-keepalive-pending))
(when (and (null jabber-keepalive-pending) (timerp jabber-keepalive-timeout-timer))
(jabber-cancel-timer jabber-keepalive-timeout-timer)
(setq jabber-keepalive-timeout-timer nil)))
(defun jabber-keepalive-timeout ()
(jabber-cancel-timer jabber-keepalive-timer)
(setq jabber-keepalive-timer nil)
(dolist (c jabber-keepalive-pending)
(message "%s: keepalive timeout, connection to %s considered lost"
(current-time-string)
(plist-get (fsm-get-state-data c) :server))
(run-hook-with-args 'jabber-lost-connection-hooks c)
(jabber-disconnect-one c nil)))
;;;; Whitespace pings - less traffic, no error checking on our side
;;;
;;; Openfire needs something like this, but I couldn't bring myself to
;;; enable keepalive by default... Whitespace pings are light and
;;; unobtrusive.
(defcustom jabber-whitespace-ping-interval 30
"Send a space character to the server with this interval, in seconds.
This is a traditional remedy for a number of problems: to keep NAT
boxes from considering the connection dead, to have the OS discover
earlier that the connection is lost, and to placate servers which rely
on the client doing this, e.g. Openfire.
If you want to verify that the server is able to answer, see
`jabber-keepalive-start' for another mechanism."
:type '(integer :tag "Interval in seconds")
:group 'jabber-core)
(defvar jabber-whitespace-ping-timer nil
"Timer object for whitespace pings")
;;;###autoload
(defun jabber-whitespace-ping-start (&optional jc)
"Start sending whitespace pings at regular intervals.
See `jabber-whitespace-ping-interval'.
The JC argument is ignored; whitespace pings are enabled for all
accounts."
(interactive)
(when jabber-whitespace-ping-timer
(jabber-whitespace-ping-stop))
(setq jabber-whitespace-ping-timer
(run-with-timer 5
jabber-whitespace-ping-interval
'jabber-whitespace-ping-do))
(add-hook 'jabber-post-disconnect-hook 'jabber-whitespace-ping-stop))
(defun jabber-whitespace-ping-stop ()
"Deactivate whitespace pings"
(interactive)
(when jabber-whitespace-ping-timer
(jabber-cancel-timer jabber-whitespace-ping-timer)
(setq jabber-whitespace-ping-timer nil)))
(defun jabber-whitespace-ping-do ()
(dolist (c jabber-connections)
(ignore-errors (jabber-send-string c " "))))
(provide 'jabber-keepalive)
;;; arch-tag: d19ca743-75a1-475f-9217-83bd18012146

View File

@ -1,62 +0,0 @@
;; jabber-keymap.el - common keymap for many modes
;; Copyright (C) 2003, 2004, 2007, 2008 - Magnus Henoch - mange@freemail.hu
;; Copyright (C) 2002, 2003, 2004 - tom berger - object@intelectronica.net
;; This file is a part of jabber.el.
;; 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
;; button.el was introduced in Emacs 22
(condition-case e
(require 'button)
(error nil))
(defvar jabber-common-keymap
(let ((map (make-sparse-keymap)))
(define-key map "\C-c\C-c" 'jabber-popup-chat-menu)
(define-key map "\C-c\C-r" 'jabber-popup-roster-menu)
(define-key map "\C-c\C-i" 'jabber-popup-info-menu)
(define-key map "\C-c\C-m" 'jabber-popup-muc-menu)
(define-key map "\C-c\C-s" 'jabber-popup-service-menu)
;; note that {forward,backward}-button are not autoloaded.
;; thus the `require' above.
(when (fboundp 'forward-button)
(define-key map [?\t] 'forward-button)
(define-key map [backtab] 'backward-button))
map))
;;;###autoload
(defvar jabber-global-keymap
(let ((map (make-sparse-keymap)))
(define-key map "\C-c" 'jabber-connect-all)
(define-key map "\C-d" 'jabber-disconnect)
(define-key map "\C-r" 'jabber-switch-to-roster-buffer)
(define-key map "\C-j" 'jabber-chat-with)
(define-key map "\C-l" 'jabber-activity-switch-to)
(define-key map "\C-a" 'jabber-send-away-presence)
(define-key map "\C-o" 'jabber-send-default-presence)
(define-key map "\C-x" 'jabber-send-xa-presence)
(define-key map "\C-p" 'jabber-send-presence)
map)
"Global Jabber keymap (usually under C-x C-j)")
;;;###autoload
(define-key ctl-x-map "\C-j" jabber-global-keymap)
(provide 'jabber-keymap)
;;; arch-tag: 22a9993d-a4a7-40ef-a025-7cff6c3f5587

View File

@ -1,103 +0,0 @@
;; jabber-libnotify.el - emacs-jabber interface to libnotify
;; Copyright (C) 2010 - Kirill A. Korinskiy - catap@catap.ru
;; Copyright (C) 2007 - Rodrigo Lazo - rlazo.paz@gmail.com
;; 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
(require 'dbus nil t)
(eval-when-compile (require 'jabber-alert))
(defcustom jabber-libnotify-icon ""
"Icon to be used on the notification pop-up. Default is empty"
:type '(file :must-match t)
:group 'jabber-alerts)
(defcustom jabber-libnotify-timeout 2500
"Specifies the timeout of the pop up window in millisecond"
:type 'integer
:group 'jabber-alerts)
(defcustom jabber-libnotify-message-header "Jabber message"
"Defines the header of the pop up."
:type 'string
:group 'jabber-alerts)
(defcustom jabber-libnotify-app "Emacs Jabber"
"Defines the app of the pop up."
:type 'string
:group 'jabber-alerts)
(defcustom jabber-libnotify-urgency "low"
"Urgency of libnotify message"
:type '(choice (const :tag "Low" "low")
(const :tag "Normal" "normal")
(const :tag "Critical" "critical"))
:group 'jabber-alerts)
(defcustom jabber-libnotify-method (if (featurep 'dbus) 'dbus 'shell)
"Specifies the method for libnotify call. Dbus is more faster but require emacs23+"
:type '(choice (const :tag "Shell" shell)
(const :tag "D-Bus" dbus))
:group 'jabber-alerts)
(defvar jabber-libnotify-id 0)
(defun jabber-libnotify-next-id ()
"Return the next notification id."
(setq jabber-libnotify-id (+ jabber-libnotify-id 1)))
(defun jabber-libnotify-message (text &optional title)
"Show MSG using libnotify"
(let
((body (or (jabber-escape-xml text) " "))
(head (jabber-escape-xml
(or title
(or jabber-libnotify-message-header " ")
text))))
;; Possible errors include not finding the notify-send binary.
(condition-case e
(cond
((eq jabber-libnotify-method 'shell)
(let ((process-connection-type nil))
(start-process "notification" nil "notify-send"
"-t" (format "%s" jabber-libnotify-timeout)
"-i" (or jabber-libnotify-icon "\"\"")
"-u" jabber-libnotify-urgency
head body)))
((eq jabber-libnotify-method 'dbus)
(dbus-call-method
:session ; use the session (not system) bus
"org.freedesktop.Notifications" ; service name
"/org/freedesktop/Notifications" ; path name
"org.freedesktop.Notifications" "Notify" ; Method
jabber-libnotify-app
(jabber-libnotify-next-id)
jabber-libnotify-icon
':string (encode-coding-string head 'utf-8)
':string (encode-coding-string body 'utf-8)
'(:array)
'(:array :signature "{sv}")
':int32 jabber-libnotify-timeout)))
(error nil))))
(define-jabber-alert libnotify "Show a message through the libnotify interface"
'jabber-libnotify-message)
(define-personal-jabber-alert jabber-muc-libnotify)
(provide 'jabber-libnotify)
;; arch-tag: e9c4c210-8245-11dd-bddf-000a95c2fcd0

View File

@ -1,83 +0,0 @@
;; jabber-logon.el - logon functions
;; Copyright (C) 2003, 2004, 2007, 2008 - Magnus Henoch - mange@freemail.hu
;; Copyright (C) 2002, 2003, 2004 - tom berger - object@intelectronica.net
;; This file is a part of jabber.el.
;; 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
(require 'jabber-xml)
(require 'jabber-util)
;; In Emacs 24, sha1 is built in, so this require is only needed for
;; earlier versions. It's supposed to be a noop in Emacs 24, but
;; sometimes, for some people, it isn't, and fails with
;; (file-error "Cannot open load file" "sha1").
(unless (fboundp 'sha1)
(require 'sha1))
(defun jabber-get-auth (jc to session-id)
"Send IQ get request in namespace \"jabber:iq:auth\"."
(jabber-send-iq jc to
"get"
`(query ((xmlns . "jabber:iq:auth"))
(username () ,(plist-get (fsm-get-state-data jc) :username)))
#'jabber-do-logon session-id
#'jabber-report-success "Impossible error - auth field request"))
(defun jabber-do-logon (jc xml-data session-id)
"send username and password in logon attempt"
(let* ((digest-allowed (jabber-xml-get-children (jabber-iq-query xml-data) 'digest))
(passwd (when
(or digest-allowed
(plist-get (fsm-get-state-data jc) :encrypted)
(yes-or-no-p "Jabber server only allows cleartext password transmission! Continue? "))
(or (plist-get (fsm-get-state-data jc) :password)
(jabber-read-password (jabber-connection-bare-jid jc)))))
auth)
(if (null passwd)
(fsm-send jc :authentication-failure)
(if digest-allowed
(setq auth `(digest () ,(sha1 (concat session-id passwd))))
(setq auth `(password () ,passwd)))
;; For legacy authentication we must specify a resource.
(unless (plist-get (fsm-get-state-data jc) :resource)
;; Yes, this is ugly. Where is my encapsulation?
(plist-put (fsm-get-state-data jc) :resource "emacs-jabber"))
(jabber-send-iq jc (plist-get (fsm-get-state-data jc) :server)
"set"
`(query ((xmlns . "jabber:iq:auth"))
(username () ,(plist-get (fsm-get-state-data jc) :username))
,auth
(resource () ,(plist-get (fsm-get-state-data jc) :resource)))
#'jabber-process-logon passwd
#'jabber-process-logon nil))))
(defun jabber-process-logon (jc xml-data closure-data)
"receive login success or failure, and request roster.
CLOSURE-DATA should be the password on success and nil on failure."
(if closure-data
;; Logon success
(fsm-send jc (cons :authentication-success closure-data))
;; Logon failure
(jabber-report-success jc xml-data "Logon")
(fsm-send jc :authentication-failure)))
(provide 'jabber-logon)
;;; arch-tag: f24ebe5e-3420-44bb-af81-d4de21f378b0

View File

@ -1,207 +0,0 @@
;; jabber-menu.el - menu definitions
;; Copyright (C) 2003, 2004, 2008 - Magnus Henoch - mange@freemail.hu
;; Copyright (C) 2002, 2003, 2004 - tom berger - object@intelectronica.net
;; This file is a part of jabber.el.
;; 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
(require 'jabber-util)
(eval-when-compile (require 'cl))
;;;###autoload
(defvar jabber-menu
(let ((map (make-sparse-keymap "jabber-menu")))
(define-key-after map
[jabber-menu-connect]
'("Connect" . jabber-connect-all))
(define-key-after map
[jabber-menu-disconnect]
'(menu-item "Disconnect" jabber-disconnect
:enable (bound-and-true-p jabber-connections)))
(define-key-after map
[jabber-menu-status]
`(menu-item "Set Status" ,(make-sparse-keymap "set-status")
:enable (bound-and-true-p jabber-connections)))
(define-key map
[jabber-menu-status jabber-menu-status-chat]
'(menu-item
"Chatty"
(lambda ()
(interactive)
(jabber-send-presence "chat"
(jabber-read-with-input-method "status message: " *jabber-current-status* '*jabber-status-history*)
*jabber-current-priority*))
:button (:radio . (and (boundp '*jabber-current-show*)
(equal *jabber-current-show* "chat")))))
(define-key map
[jabber-menu-status jabber-menu-status-dnd]
'(menu-item
"Do not Disturb"
(lambda ()
(interactive)
(jabber-send-presence "dnd"
(jabber-read-with-input-method "status message: " *jabber-current-status* '*jabber-status-history*)
*jabber-current-priority*))
:button (:radio . (and (boundp '*jabber-current-show*)
(equal *jabber-current-show* "dnd")))))
(define-key map
[jabber-menu-status jabber-menu-status-xa]
'(menu-item "Extended Away" jabber-send-xa-presence
:button (:radio . (and (boundp '*jabber-current-show*)
(equal *jabber-current-show* "xa")))))
(define-key map
[jabber-menu-status jabber-menu-status-away]
'(menu-item "Away" jabber-send-away-presence
:button (:radio . (and (boundp '*jabber-current-show*)
(equal *jabber-current-show* "away")))))
(define-key map
[jabber-menu-status jabber-menu-status-online]
'(menu-item "Online" jabber-send-default-presence
:button (:radio . (and (boundp '*jabber-current-show*)
(equal *jabber-current-show* "")))))
(define-key-after map
[separator]
'(menu-item "--"))
(define-key-after map
[jabber-menu-chat-with]
'(menu-item "Chat with..." jabber-chat-with
:enable (bound-and-true-p jabber-connections)))
(define-key-after map
[jabber-menu-nextmsg]
'(menu-item "Next unread message" jabber-activity-switch-to
:enable (bound-and-true-p jabber-activity-jids)))
(define-key-after map
[jabber-menu-send-subscription-request]
'(menu-item "Send subscription request" jabber-send-subscription-request
:enable (bound-and-true-p jabber-connections)))
(define-key-after map
[jabber-menu-roster]
'("Switch to roster" . jabber-switch-to-roster-buffer))
(define-key-after map
[separator2]
'(menu-item "--"))
(define-key-after map
[jabber-menu-customize]
'("Customize" . jabber-customize))
(define-key-after map
[jabber-menu-info]
'("Help" . jabber-info))
map))
;;;###autoload
(defcustom jabber-display-menu 'maybe
"Decide whether the \"Jabber\" menu is displayed in the menu bar.
If t, always display.
If nil, never display.
If maybe, display if jabber.el is installed under `package-user-dir', or
if any of `jabber-account-list' or `jabber-connections' is non-nil."
:group 'jabber
:type '(choice (const :tag "Never" nil)
(const :tag "Always" t)
(const :tag "When installed by user, or when any accounts have been configured or connected" maybe)))
(defun jabber-menu (&optional remove)
"Put \"Jabber\" menu on menubar.
With prefix argument, remove it."
(interactive "P")
(setq jabber-display-menu (if remove nil t))
(force-mode-line-update))
(make-obsolete 'jabber-menu "set the variable `jabber-display-menu' instead.")
;; This used to be:
;; (define-key-after global-map [menu-bar jabber-menu] ...)
;; but that doesn't work in Emacs 21.
;;;###autoload
(define-key-after (lookup-key global-map [menu-bar])
[jabber-menu]
(list 'menu-item "Jabber" jabber-menu
:visible
'(or (eq jabber-display-menu t)
(and (eq jabber-display-menu 'maybe)
(or (bound-and-true-p jabber-account-list)
(bound-and-true-p jabber-connections))))))
(defvar jabber-jid-chat-menu nil
"Menu items for chat menu")
(defvar jabber-jid-info-menu nil
"Menu item for info menu")
(defvar jabber-jid-roster-menu nil
"Menu items for roster menu")
(defvar jabber-jid-muc-menu nil
"Menu items for MUC menu")
(defvar jabber-jid-service-menu nil
"Menu items for service menu")
(defun jabber-popup-menu (which-menu)
"Popup specified menu"
(let* ((mouse-event (and (listp last-input-event) last-input-event))
(choice (widget-choose "Actions" which-menu mouse-event)))
(if mouse-event
(mouse-set-point mouse-event))
(if choice
(call-interactively choice))))
(defun jabber-popup-chat-menu ()
"Popup chat menu"
(interactive)
(jabber-popup-menu jabber-jid-chat-menu))
(defun jabber-popup-info-menu ()
"Popup info menu"
(interactive)
(jabber-popup-menu jabber-jid-info-menu))
(defun jabber-popup-roster-menu ()
"Popup roster menu"
(interactive)
(jabber-popup-menu jabber-jid-roster-menu))
(defun jabber-popup-muc-menu ()
"Popup MUC menu"
(interactive)
(jabber-popup-menu jabber-jid-muc-menu))
(defun jabber-popup-service-menu ()
"Popup service menu"
(interactive)
(jabber-popup-menu jabber-jid-service-menu))
(defun jabber-popup-combined-menu ()
"Popup combined menu"
(interactive)
(jabber-popup-menu (append jabber-jid-chat-menu jabber-jid-info-menu jabber-jid-roster-menu jabber-jid-muc-menu)))
(provide 'jabber-menu)
;;; arch-tag: 5147f52f-de47-4348-86ff-b799d7a75e3f

View File

@ -1,98 +0,0 @@
;; jabber-modeline.el - display jabber status in modeline
;; Copyright (C) 2004 - Magnus Henoch - mange@freemail.hu
;; This file is a part of jabber.el.
;; 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
(require 'jabber-presence)
(require 'jabber-alert)
(eval-when-compile (require 'cl))
(defgroup jabber-mode-line nil
"Display Jabber status in mode line"
:group 'jabber)
(defcustom jabber-mode-line-compact t
"Count contacts in fewer categories for compact view"
:group 'jabber-mode-line
:type 'boolean)
(defvar jabber-mode-line-string nil)
(defvar jabber-mode-line-presence nil)
(defvar jabber-mode-line-contacts nil)
(defadvice jabber-send-presence (after jsp-update-mode-line
(show status priority))
(jabber-mode-line-presence-update))
(defun jabber-mode-line-presence-update ()
(setq jabber-mode-line-presence (if (and jabber-connections (not *jabber-disconnecting*))
(cdr (assoc *jabber-current-show* jabber-presence-strings))
"Offline")))
(defun jabber-mode-line-count-contacts (&rest ignore)
(let ((count (list (cons "chat" 0)
(cons "" 0)
(cons "away" 0)
(cons "xa" 0)
(cons "dnd" 0)
(cons nil 0))))
(dolist (jc jabber-connections)
(dolist (buddy (plist-get (fsm-get-state-data jc) :roster))
(when (assoc (get buddy 'show) count)
(incf (cdr (assoc (get buddy 'show) count))))))
(setq jabber-mode-line-contacts
(if jabber-mode-line-compact
(format "(%d/%d/%d)"
(+ (cdr (assoc "chat" count))
(cdr (assoc "" count)))
(+ (cdr (assoc "away" count))
(cdr (assoc "xa" count))
(cdr (assoc "dnd" count)))
(cdr (assoc nil count)))
(apply 'format "(%d/%d/%d/%d/%d/%d)"
(mapcar 'cdr count))))))
(define-minor-mode jabber-mode-line-mode
"Toggle display of Jabber status in mode lines.
Display consists of your own status, and six numbers
meaning the number of chatty, online, away, xa, dnd
and offline contacts, respectively."
:global t :group 'jabber-mode-line
(setq jabber-mode-line-string "")
(or global-mode-string (setq global-mode-string '("")))
(if jabber-mode-line-mode
(progn
(add-to-list 'global-mode-string 'jabber-mode-line-string t)
(setq jabber-mode-line-string (list " "
'jabber-mode-line-presence
" "
'jabber-mode-line-contacts))
(put 'jabber-mode-line-string 'risky-local-variable t)
(put 'jabber-mode-line-presence 'risky-local-variable t)
(jabber-mode-line-presence-update)
(jabber-mode-line-count-contacts)
(ad-activate 'jabber-send-presence)
(add-hook 'jabber-post-disconnect-hook
'jabber-mode-line-presence-update)
(add-hook 'jabber-presence-hooks
'jabber-mode-line-count-contacts))))
(provide 'jabber-modeline)
;;; arch-tag: c03a7d3b-8811-49d4-b0e0-7ffd661d7925

View File

@ -1,85 +0,0 @@
;;; jabber-muc-nick-coloring.el --- Add nick coloring abilyty to emacs-jabber
;; Copyright 2009, 2010, 2012, 2013 Terechkov Evgenii - evg@altlinux.org
;; 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, 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
;;; Commentary:
;;; Code:
(eval-when-compile (require 'cl)) ;for ignore-errors
;; we need hexrgb-hsv-to-hex:
(eval-and-compile
(or (ignore-errors (require 'hexrgb))
;; jabber-fallback-lib/ from jabber/lisp/jabber-fallback-lib
(ignore-errors
(let ((load-path (cons (expand-file-name
"jabber-fallback-lib"
(file-name-directory (locate-library "jabber")))
load-path)))
(require 'hexrgb)))
(error
"hexrgb not found in `load-path' or jabber-fallback-lib/ directory.")))
;;;;##########################################################################
;;;; User Options, Variables
;;;;##########################################################################
(defcustom jabber-muc-participant-colors nil
"Alist of used colors. Format is (nick . color). Color may be
in #RGB or textual (like red or blue) notation. Colors will be
added in #RGB notation for unknown nicks."
:type '(alist :key-type string :value-type color)
:group 'jabber-chat)
(defcustom jabber-muc-colorize-local nil
"Colorize MUC messages from you."
:type 'boolean
:group 'jabber-chat)
(defcustom jabber-muc-colorize-foreign nil
"Colorize MUC messages not from you."
:type 'boolean
:group 'jabber-chat)
(defcustom jabber-muc-nick-saturation 1.0
"Default saturation for nick coloring."
:type 'float
:group 'jabber-chat)
(defcustom jabber-muc-nick-value 1.0
"Default value for nick coloring."
:type 'float
:group 'jabber-chat)
(defun jabber-muc-nick-gen-color (nick)
"Return good enough color from available pool"
(let ((hue (/ (mod (string-to-number (substring (md5 nick) 0 6) 16) 360) 360.0)))
(hexrgb-hsv-to-hex hue jabber-muc-nick-saturation jabber-muc-nick-value)))
(defun jabber-muc-nick-get-color (nick)
"Get NICKs color"
(let ((color (cdr (assoc nick jabber-muc-participant-colors))))
(if color
color
(progn
(unless jabber-muc-participant-colors )
(push (cons nick (jabber-muc-nick-gen-color nick)) jabber-muc-participant-colors)
(cdr (assoc nick jabber-muc-participant-colors))))))
(provide 'jabber-muc-nick-coloring)
;;; jabber-muc-nick-coloring.el ends here

View File

@ -1,188 +0,0 @@
;;; jabber-muc-nick-completion.el --- Add nick completion abilyty to emacs-jabber
;; Copyright (C) 2008 - Terechkov Evgenii - evg@altlinux.org
;; Copyright (C) 2007, 2008, 2010 - Kirill A. Korinskiy - catap@catap.ru
;; Copyright (C) 2007 - Serguei Jidkov - jsv@e-mail.ru
;; This file is a part of jabber.el.
;; 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
;;; User customizations here:
(defcustom jabber-muc-completion-delimiter ": "
"String to add to end of completion line."
:type 'string
:group 'jabber-chat)
(defcustom jabber-muc-looks-personaling-symbols '("," ":" ">")
"Symbols for personaling messages"
:type '(repeat string)
:group 'jabber-chat)
(defcustom jabber-muc-personal-message-bonus (* 60 20)
"Bonus for personal message, in seconds."
:type 'integer
:group 'jabber-chat)
(defcustom jabber-muc-all-string "all"
"String meaning all conference members (to insert in completion). Note that \":\" or alike not needed (it appended in other string)"
:type 'string
:group 'jabber-chat)
;;; History:
;;
;;; Code:
(require 'cl)
(require 'jabber-muc)
(require 'hippie-exp)
(defvar *jabber-muc-participant-last-speaking* nil
"Global alist in form (group . ((member . time-of-last-speaking) ...) ...).")
(defun jabber-my-nick (&optional group)
"Return my jabber nick in GROUP."
(let ((room (or group jabber-group)))
(cdr (or (assoc room *jabber-active-groupchats*)
(assoc room jabber-muc-default-nicknames)))
))
;;;###autoload
(defun jabber-muc-looks-like-personal-p (message &optional group)
"Return non-nil if jabber MESSAGE is addresed to me.
Optional argument GROUP to look."
(if message (string-match (concat
"^"
(jabber-my-nick group)
(regexp-opt jabber-muc-looks-personaling-symbols))
message)
nil))
(defun jabber-muc-nicknames ()
"List of conference participants, excluding self, or nil if we not in conference."
(delete-if '(lambda (nick)
(string= nick (jabber-my-nick)))
(append (mapcar 'car (cdr (assoc jabber-group jabber-muc-participants))) (list jabber-muc-all-string))))
(defun jabber-muc-participant-update-activity (group nick time)
"Updates NICK's time of last speaking in GROUP to TIME."
(let* ((room (assoc group *jabber-muc-participant-last-speaking*))
(room-activity (cdr room))
(entry (assoc nick room-activity))
(old-time (or (cdr entry) 0)))
(when (> time old-time)
;; don't use put-alist for speed
(progn
(if entry (setcdr entry time)
(setq room-activity
(cons (cons nick time) room-activity)))
(if room (setcdr room room-activity)
(setq *jabber-muc-participant-last-speaking*
(cons (cons group room-activity)
*jabber-muc-participant-last-speaking*)))))))
(defun jabber-muc-track-message-time (nick group buffer text &optional title)
"Tracks time of NICK's last speaking in GROUP."
(when nick
(let ((time (float-time)))
(jabber-muc-participant-update-activity
group
nick
(if (jabber-muc-looks-like-personal-p text group)
(+ time jabber-muc-personal-message-bonus)
time)))))
(defun jabber-sort-nicks (nicks group)
"Return list of NICKS in GROUP, sorted."
(let ((times (cdr (assoc group *jabber-muc-participant-last-speaking*))))
(flet ((fetch-time (nick) (or (assoc nick times) (cons nick 0)))
(cmp (nt1 nt2)
(let ((t1 (cdr nt1))
(t2 (cdr nt2)))
(if (and (zerop t1) (zerop t2))
(string<
(car nt1)
(car nt2))
(> t1 t2)))))
(mapcar 'car (sort (mapcar 'fetch-time nicks)
'cmp)))))
(defun jabber-muc-beginning-of-line ()
"Return position of line begining."
(save-excursion
(if (looking-back jabber-muc-completion-delimiter)
(backward-char (+ (length jabber-muc-completion-delimiter) 1)))
(skip-syntax-backward "^-")
(point)))
;;; One big hack:
(defun jabber-muc-completion-delete-last-tried ()
"Delete last tried competion variand from line."
(let ((last-tried (car he-tried-table)))
(when last-tried
(goto-char he-string-beg)
(delete-char (length last-tried))
(ignore-errors (delete-char (length jabber-muc-completion-delimiter)))
)))
(defun try-expand-jabber-muc (old)
"Try to expand target nick in MUC according to last speaking time.
OLD is last tried nickname."
(unless jabber-chatting-with
(unless old
(let ((nicknames (jabber-muc-nicknames)))
(he-init-string (jabber-muc-beginning-of-line) (point))
(setq he-expand-list (jabber-sort-nicks (all-completions he-search-string (mapcar 'list nicknames)) jabber-group))))
(setq he-expand-list
(delete-if '(lambda (x)
(he-string-member x he-tried-table))
he-expand-list))
(if (null he-expand-list)
(progn
(when old
;; here and later : its hack to workaround
;; he-substitute-string work which cant substitute empty
;; lines
(if (string= he-search-string "")
(jabber-muc-completion-delete-last-tried)
(he-reset-string)))
())
(let ((subst (if (eq (line-beginning-position) (jabber-muc-beginning-of-line))
(concat (car he-expand-list) jabber-muc-completion-delimiter)
(car he-expand-list))))
(if (not (string= he-search-string ""))
(he-substitute-string subst)
(jabber-muc-completion-delete-last-tried)
(progn
(insert subst)
(if (looking-back (concat "^" (regexp-quote (car he-expand-list))))
(unless (looking-back (concat "^" (regexp-quote (car he-expand-list)) jabber-muc-completion-delimiter))
(insert jabber-muc-completion-delimiter)))
)
))
(setq he-tried-table (cons (car he-expand-list) (cdr he-tried-table)))
(setq he-expand-list (cdr he-expand-list))
t)))
(add-hook 'jabber-muc-hooks 'jabber-muc-track-message-time)
(fset 'jabber-muc-completion (make-hippie-expand-function '(try-expand-jabber-muc)))
(define-key jabber-chat-mode-map [?\t] 'jabber-muc-completion)
(provide 'jabber-muc-nick-completion)
;; arch-tag: 2a81ac72-d261-11dc-be91-000a95c2fcd0
;;; jabber-muc-completion.el ends here

File diff suppressed because it is too large Load Diff

View File

@ -1,91 +0,0 @@
;; jabber-notifications.el - emacs-jabber interface to notifications.el
;; Copyright (C) 2014 - Adam Sjøgren - asjo@koldfront.dk
;; Copyright (C) 2010 - Kirill A. Korinskiy - catap@catap.ru
;; Copyright (C) 2007 - Rodrigo Lazo - rlazo.paz@gmail.com
;; 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
;; Built on jabber-libnotify.el.
(eval-when-compile (require 'jabber-alert))
(unless (string< emacs-version "24.1") ;notifications.el preset since Emacs 24.1
(require 'notifications)
(defcustom jabber-notifications-icon ""
"Icon to be used on the notification pop-up. Default is empty"
:type '(file :must-match t)
:group 'jabber-alerts)
(defcustom jabber-notifications-timeout nil
"Specifies the timeout of the pop up window in millisecond"
:type 'integer
:group 'jabber-alerts)
(defcustom jabber-notifications-message-header "Jabber message"
"Defines the header of the pop up."
:type 'string
:group 'jabber-alerts)
(defcustom jabber-notifications-app "Emacs Jabber"
"Defines the app of the pop up."
:type 'string
:group 'jabber-alerts)
(defcustom jabber-notifications-urgency "low"
"Urgency of message"
:type '(choice (const :tag "Low" "low")
(const :tag "Normal" "normal")
(const :tag "Critical" "critical"))
:group 'jabber-alerts)
(defun jabber-message-notifications (from buffer text title)
"Show a message through the notifications.el interface"
(let
((body (or (jabber-escape-xml text) " "))
(head (jabber-escape-xml
(or title
(or jabber-notifications-message-header " ")
text)))
(avatar-hash (get (jabber-jid-symbol from) 'avatar-hash)))
(notifications-notify
:title title
:body body
:app-icon (or (and avatar-hash (jabber-avatar-find-cached avatar-hash))
jabber-notifications-icon)
:app-name jabber-notifications-app
:category "jabber.message"
:timeout jabber-notifications-timeout)))
(defun jabber-muc-notifications (nick group buffer text title)
"Show MUC message through the notifications.el interface"
(jabber-message-notifications group buffer (if nick (format "%s: %s" nick text) text) title)
)
(defun jabber-muc-notifications-personal (nick group buffer text title)
"Show personal MUC message through the notifications.el interface"
(if (jabber-muc-looks-like-personal-p text group)
(jabber-muc-notifications nick group buffer text title))
)
;; jabber-*-notifications* requires "from" argument, so we cant use
;; define-jabber-alert/define-personal-jabber-alert here and do the
;; work by hand:
(pushnew 'jabber-message-notifications (get 'jabber-alert-message-hooks 'custom-options))
(pushnew 'jabber-muc-notifications (get 'jabber-alert-muc-hooks 'custom-options))
(pushnew 'jabber-muc-notifications-personal (get 'jabber-alert-muc-hooks 'custom-options))
)
(provide 'jabber-notifications)

View File

@ -1,35 +0,0 @@
;;; jabber-osd.el --- OSD support for jabber.el
;; Copyright (C) 2008 - Terechkov Evgenii - evg@altlinux.org
;; This file is a part of jabber.el.
;; 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, 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 GNU Emacs; see the file COPYING. If not, write to
;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
;; Boston, MA 02111-1307, USA.
(eval-when-compile (require 'jabber-alert))
(condition-case e
(progn
;; Most people don't have osd.el, so this will often fail
(require 'osd)
(define-jabber-alert osd "Display a message in osd"
(lambda (text &optional title) (osd-show-string (or title text))))
(define-personal-jabber-alert jabber-muc-osd))
(error nil))
(provide 'jabber-osd)
;; arch-tag: 3eb8d55a-dd86-11dc-b2c6-000a95c2fcd0

View File

@ -1,8 +0,0 @@
;; jabber-ourversion.el. Holds the version number in a format that
;; configure.ac can read.
;; On the following line, only change the part between double quotes:
(defconst jabber-version "0.8.92"
"version returned to those who query us")
(provide 'jabber-ourversion)

View File

@ -1,61 +0,0 @@
;; jabber-ping.el - XMPP "Ping" by XEP-0199
;; Copyright (C) 2009 - Evgenii Terechkov - evg@altlinux.org
;; This file is a part of jabber.el.
;; 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
(require 'jabber-iq)
(require 'jabber-util)
(require 'jabber-menu)
(require 'jabber-disco)
(add-to-list 'jabber-jid-info-menu
(cons "Ping" 'jabber-ping))
(defun jabber-ping-send (jc to process-func on-success on-error)
"Send XEP-0199 ping IQ stanza. JC is connection to use, TO is
full JID, PROCESS-FUNC is fucntion to call to process result,
ON-SUCCESS and ON-ERROR is arg for this function depending on
result."
(jabber-send-iq jc to "get"
'(ping ((xmlns . "urn:xmpp:ping")))
process-func on-success
process-func on-error))
(defun jabber-ping (to)
"Ping XMPP entity. TO is full JID. All connected JIDs is used."
(interactive (list (jabber-read-jid-completing "Send ping to: " nil nil nil 'full)))
(dolist (jc jabber-connections)
(jabber-ping-send jc to 'jabber-silent-process-data 'jabber-process-ping "Ping is unsupported")))
;; called by jabber-process-data
(defun jabber-process-ping (jc xml-data)
"Handle results from ping requests."
(let ((to (jabber-xml-get-attribute xml-data 'from)))
(format "%s is alive" to)))
(add-to-list 'jabber-iq-get-xmlns-alist (cons "urn:xmpp:ping" 'jabber-pong))
(jabber-disco-advertise-feature "urn:xmpp:ping")
(defun jabber-pong (jc xml-data)
"Return pong as defined in XEP-0199. Sender and Id are
determined from the incoming packet passed in XML-DATA."
(let ((to (jabber-xml-get-attribute xml-data 'from))
(id (jabber-xml-get-attribute xml-data 'id)))
(jabber-send-iq jc to "result" nil nil nil nil nil id)))
(provide 'jabber-ping)

View File

@ -1,7 +0,0 @@
(define-package "jabber" "20180927.2325" "A Jabber client for Emacs."
'((fsm "0.2")
(srv "0.2"))
:commit "fff33826f42e040dad7ef64ea312d85215d3b0a1")
;; Local Variables:
;; no-byte-compile: t
;; End:

Some files were not shown because too many files have changed in this diff Show More