1 ;;; xlib-xrecord.el --- RECORD extension for xlib.
3 ;; Copyright (C) 2003-2005 by XWEM Org.
5 ;; Author: Zajcev Evgeny <zevlg@yandex.ru>
6 ;; Created: 18 October 2003
7 ;; Keywords: xlib, xwem
8 ;; X-CVS: $Id: xlib-xrecord.el,v 1.8 2005-04-04 19:55:30 lg Exp $
10 ;; This file is part of XWEM.
12 ;; XWEM is free software; you can redistribute it and/or modify it
13 ;; under the terms of the GNU General Public License as published by
14 ;; the Free Software Foundation; either version 2, or (at your option)
17 ;; XWEM is distributed in the hope that it will be useful, but WITHOUT
18 ;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
19 ;; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
20 ;; License for more details.
22 ;; You should have received a copy of the GNU General Public License
23 ;; along with XEmacs; see the file COPYING. If not, write to the Free
24 ;; Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
27 ;;; Synched up with: Not in FSF
31 ;; We need to open two connections to X server to use RECORD
32 ;; extension, one for RC controling and second for data transfer,
33 ;; `X-XRecordEnableContext' should be issued on data connection.
35 ;; Range8, Range16 is cons cells in form (FIRST . LAST)
37 ;; ExtRange is cons cell in form (MAJOR-Range8 . MINOR-Range16)
46 (defconst X-XRecord-major 1)
47 (defconst X-XRecord-minor 13)
49 (defconst X-XRecord-op-QueryVersion 0)
50 (defconst X-XRecord-op-CreateContext 1)
51 (defconst X-XRecord-op-RegisterClients 2)
52 (defconst X-XRecord-op-UnregisterClients 3)
53 (defconst X-XRecord-op-GetContext 4)
54 (defconst X-XRecord-op-EnableContext 5)
55 (defconst X-XRecord-op-DisplayContext 6)
56 (defconst X-XRecord-op-FreeContext 7)
59 (defconst X-XRecordFromServerTime (Xmask 0))
60 (defconst X-XRecordFromClientTime (Xmask 1))
61 (defconst X-XRecordFromClientSequence (Xmask 2))
63 (defconst X-XRecordCurrentClients 1)
64 (defconst X-XRecordFutureClients 2)
65 (defconst X-XRecordAllClients 3)
67 (defconst X-XRecordFromServer 0)
68 (defconst X-XRecordFromClient 1)
69 (defconst X-XRecordClientStarted 2)
70 (defconst X-XRecordClientDied 3)
71 (defconst X-XRecordStartOfData 4)
72 (defconst X-XRecordEndOfData 5)
76 (defsubst X-RecordRange8-message (xrr8)
77 "Return a string representing the record range8 XRR8."
80 (concat (int->string1 (car xrr8)) (int->string1 (cdr xrr8)))))
82 (defsubst X-RecordRange16-message (xrr16)
83 "Return a string representing the record range16 XRR16."
86 (concat (int->string2 (car xrr16)) (int->string2 (cdr xrr16)))))
88 (defsubst X-RecordExtrange-message (xer)
89 "Return a string representing the extrange XER."
91 (make-string 12 ?\x00)
92 (concat (X-RecordRange8-message (car xer)) (X-RecordRange16-message (cdr xer)))))
94 (defsubst X-RecordRange-message (xrr)
95 "Return a string representing the record range XRR."
96 (X-Generate-simple-message 'X-RecordRange xrr))
98 (defsubst X-RecordClientSpec-message (xrcs)
99 "Return a string representing the client spec XRCS."
102 (defstruct (X-RecordContext (:predicate X-RecordContext-isrc-p))
104 props) ; User defined plist
106 (defstruct (X-RecordExtrange (:predicate X-RecordExtrange-isrer-p))
107 major ; X-RecordRange8
108 minor ; X-RecordRange16
109 ;; List of extractors
110 (list '(((lambda (re)
111 (X-RecordRange8-message (X-RecordExtrange-major re))) . 2)
113 (X-RecordRange16-message (X-RecordExtrange-minor re))) . 4)))
116 (defstruct (X-RecordRange (:predicate X-RecordRange-isrr-p))
117 core-requests ; X-RecordRange8
118 core-replies ; X-RecordRange8
119 ext-requests ; X-RecordExtrange
120 ext-replies ; X-RecordExtrange
121 delivered-events ; X-RecordRange8
122 device-events ; X-RecordRange8
123 errors ; X-RecordRange8
124 client-started ; BOOL
126 ;; List of extractors
129 (X-RecordRange8-message (X-RecordRange-core-requests rr)))
132 (X-RecordRange8-message (X-RecordRange-core-replies rr)))
135 (X-RecordExtrange-message (X-RecordRange-ext-requests rr)))
138 (X-RecordExtrange-message (X-RecordRange-ext-replies rr)))
141 (X-RecordRange8-message (X-RecordRange-delivered-events rr)))
144 (X-RecordRange8-message (X-RecordRange-device-events rr)))
147 (X-RecordRange8-message (X-RecordRange-errors rr)))
149 (cons 'X-RecordRange-client-started 1)
150 (cons 'X-RecordRange-client-died 1))))
152 (defstruct (X-RecordClientInfo (:predicate X-RecordClientInfo-isrci-p))
153 client-spec ; X-RecordClientSpec
154 ranges) ; list of X-RecordRange
156 (defstruct X-RecordState
159 client-infos ; list of X-RecordClientInfo
164 (defsubst X-RecordContext-p (xrc &optional sig)
165 (X-Generic-p 'X-RecordContext 'X-RecordContext-isrc-p xrc sig))
167 (defsubst X-RecordRange8-p (xrr8 &optional sig)
168 (or (null xrr8) (consp xrr8)))
170 (defsubst X-RecordRange16-p (xrr16 &optional sig)
171 (or (null xrr16) (consp xrr16)))
173 (defsubst X-RecordExtrange-p (xrer &optional sig)
174 (or (null xrer) (and (consp xrer) (X-RecordRange8-p (car xrer)) (X-RecordRange16-p (cdr xrer)))))
176 (defsubst X-RecordRange-p (xrr &optional sig)
177 (X-Generic-p 'X-RecordRange 'X-RecordRange-isrr-p xrr sig))
179 (defsubst X-RecordClientSpec-p (xrcs &optional sig)
182 (defsubst X-RecordClientInfo-p (xrci &optional sig)
183 (X-Generic-p 'X-RecordClientInfo 'X-RecordClientInfo-isrci-p xrci sig))
187 (defun X-XRecordQueryVersion (xdpy &optional major minor)
188 "On display XDPY query for version of record extension."
189 (X-Dpy-p xdpy 'X-XRecordQueryVersion)
191 (let* ((xrec-ext (X-Dpy-get-extension xdpy "RECORD"))
193 (list (vector 1 (nth 4 xrec-ext)) ; opcode
194 [1 X-XRecord-op-QueryVersion]
197 [2 (or major X-XRecord-major)]
198 [2 (or minor X-XRecord-minor)]))
199 (msg (X-Create-message ListOfFields))
201 (list [1 success] ;success field
203 (list [1 nil] ;not used
204 [2 integerp] ;sequence number
206 [2 integerp] ;major version
207 [2 integerp] ;minor version
210 (X-Dpy-send-read xdpy msg ReceiveFields))))
212 (defun X-XRecordCreateContext (xdpy rc elhead clspecs ranges)
213 "ELHEAD is contructed using `Xmask-or' and values
214 `X-XRecordFromServerTime', `X-XRecordFromClient' and
215 `X-XRecordFromClientSequence'.
217 CLSPECS is list of X-RecordClientSpec
218 RANGES is list of X-RecordRange."
219 (X-Dpy-p xdpy 'X-XRecordCreateContext)
221 (let* ((xrec-ext (X-Dpy-get-extension xdpy "RECORD" 'X-XRecordCreateContext))
223 (list (vector 1 (nth 4 xrec-ext)) ;opcode
224 [1 X-XRecord-op-CreateContext]
225 [2 (+ 5 (length clspecs) (* 6 (length ranges)))] ;length
227 [4 (X-RecordContext-id rc)] ; context
231 [4 (length ranges)]))
232 (msg (concat (X-Create-message ListOfFields)
233 (X-Generate-message-for-list clspecs 'X-RecordClientSpec-message)
234 (X-Generate-message-for-list ranges 'X-RecordRange-message))))
235 (X-Dpy-send xdpy msg)
238 (defun X-XRecordRegisterClients (xdpy rc elhead clspecs ranges)
239 "On display XDPY, register CLSPECS for intercepting in record context RC."
240 (X-Dpy-p xdpy 'X-XRecordRegisterClients)
241 (X-RecordContext-p rc 'X-XRecordRegisterClients)
243 (let* ((xrec-ext (X-Dpy-get-extension xdpy "RECORD" 'X-XRecordRegisterClients))
245 (list (vector 1 (nth 4 xrec-ext)) ; opcode
246 [1 X-XRecord-op-RegisterClients]
247 [2 (+ 5 (length clspecs) (* 6 (length ranges)))] ; length
248 [4 (X-RecordContext-id rc)]
252 [4 (length ranges)]))
253 (msg (concat (X-Create-message ListOfFields)
254 (X-Generate-message-for-list clspecs 'X-RecordClientSpec-message)
255 (X-Generate-message-for-list ranges 'X-RecordRange-message))))
256 (X-Dpy-send xdpy msg)))
258 (defun X-XRecordUnregisterClients (xdpy rc clspecs)
259 "On display XDPY in record context RC unregister clients in CLSPECS."
260 (X-Dpy-p xdpy 'X-XRecordRegisterClients)
261 (X-RecordContext-p rc 'X-XRecordRegisterClients)
263 (let* ((xrec-ext (X-Dpy-get-extension xdpy "RECORD" 'X-XRecordRegisterClients))
265 (list (vector 1 (nth 4 xrec-ext)) ; opcode
266 [1 X-XRecord-op-UnregisterClients]
267 [2 (+ 3 (length clspecs))] ; length
268 [4 (X-RecordContext-id rc)]
269 [4 (length clspecs)]))
270 (msg (concat (X-Create-message ListOfFields)
271 (X-Generate-message-for-list clspecs 'X-RecordClientSpec-message))))
272 (X-Dpy-send xdpy msg)))
274 (defun X-XRecordGetContext (xdpy rc)
275 "On display XDPY get context for RC."
276 (X-Dpy-p xdpy 'X-XRecordGetContext)
277 (X-RecordContext-p rc 'X-XRecordRegisterClients)
279 (let* ((xrec-ext (X-Dpy-get-extension xdpy "RECORD" 'X-XRecordGetContext))
281 (list (vector 1 (nth 4 xrec-ext)) ; opcode
282 [1 X-XRecord-op-GetContext]
284 [4 (X-RecordContext-id rc)])) ; context
285 (msg (concat (X-Create-message ListOfFields)))
287 (list [1 success] ;success field
289 (list [1 integerp] ;enabled
290 [2 integerp] ;sequence number
294 [4 length-2] ;n, number of intercepted-clients
296 [length-2 ([4 integerp]
327 (X-Dpy-send-read xdpy msg ReceiveFields)))
328 ; (X-log dpy "Get X-XRecordGetContext replay: %s\n" 'resp)
330 (defun X-XRecordEnableContext (xdpy rc)
331 "On display XDPY enable RC context.
333 This request enables data transfer between the recording client, and
334 the extension and returns the protocol data the recording client has
335 previously expressed interest in. Typically, this request is executed
336 by the recording client over the data connection."
338 (X-Dpy-p xdpy 'X-XRecordEnableContext)
340 (let* ((xrec-ext (X-Dpy-get-extension xdpy "RECORD" 'X-XRecordEnableContext))
342 (list (vector 1 (nth 4 xrec-ext)) ;opcode
343 [1 X-XRecord-op-EnableContext]
345 [4 (X-RecordContext-id rc)]))
346 (msg (concat (X-Create-message ListOfFields)))
348 (list [1 success] ;success field
350 (list [1 integerp] ;category
351 [2 integerp] ;sequence number
354 [1 integerp] ;client-swapped
356 [4 integerp] ;id-baes
357 [4 integerp] ;server-time
358 [4 integerp] ;recorded sequence number
360 [(* length-1 4) stringp])))
361 (rep (X-Dpy-send-read xdpy msg ReceiveFields)))
363 (X-Dpy-log xdpy 'x-record "X-XRecordEnableContext: rep=%S" 'rep)
365 (= (nth 1 rep) X-XRecordStartOfData))
366 ;; Set events guess parser and events dispatcher
367 (setf (X-Dpy-parse-guess-dispatcher xdpy) 'X-XRecord-parse-guess)
368 (setf (X-Dpy-events-dispatcher xdpy) 'X-XRecord-event-dispatcher))
371 (defun X-XRecordDisableContext (xdpy rc)
372 "On display XDPY disable recording context RC."
373 (X-Dpy-p xdpy 'X-XRecordGetContext)
374 (X-RecordContext-p rc 'X-XRecordRegisterClients)
376 (let* ((xrec-ext (X-Dpy-get-extension xdpy "RECORD" 'X-XRecordGetContext))
378 (list (vector 1 (nth 4 xrec-ext)) ; opcode
379 [1 X-XRecord-op-DisplayContext]
381 [4 (X-RecordContext-id rc)])) ; context
382 (msg (X-Create-message ListOfFields)))
383 (X-Dpy-send xdpy msg))
384 (X-Dpy-log xdpy 'x-record "X-XRecordDisableContext: rc=%S" '(X-RecordContext-id rc)))
386 (defun X-XRecordFreeContext (xdpy rc)
387 "On display XDPY free record context RC."
388 (X-Dpy-p xdpy 'X-XRecordGetContext)
389 (X-RecordContext-p rc 'X-XRecordRegisterClients)
391 (let* ((xrec-ext (X-Dpy-get-extension xdpy "RECORD" 'X-XRecordGetContext))
393 (list (vector 1 (nth 4 xrec-ext)) ; opcode
394 [1 X-XRecord-op-FreeContext]
396 [4 (X-RecordContext-id rc)])) ; context
397 (msg (X-Create-message ListOfFields)))
398 (X-Dpy-send xdpy msg)))
401 (defun X-XRecord-event-dispatcher (xdpy win xev)
402 "Dispatch XEvent received fro XRECORD data connection."
403 (setf (X-Dpy-evq xdpy)
404 (append (X-Dpy-evq xdpy) (list xev))))
406 (defun X-XRecord-parse-guess (xdpy)
407 "Parse message received in data connection."
408 (X-Dpy-p xdpy 'X-XRecord-parse-guess)
410 (while (and (zerop (X-Dpy-readings xdpy))
411 (> (length (X-Dpy-message-buffer xdpy)) 31))
412 (X-Dpy-read-excursion xdpy
413 (let* ((msg (X-Dpy-parse-message
414 (list [1 integerp] ; reply
415 [1 integerp] ;category
416 [2 integerp] ;sequence number
419 [1 integerp] ;client-swapped
421 [4 integerp] ;id-baes
422 [4 integerp] ;server-time
423 [4 integerp] ;recorded sequence number
426 (mcategory (nth 1 msg)) ; message categery
427 (len (* 4 (nth 3 msg)))
438 (X-Dpy-parse-message (list [4 integerp]) 0 xdpy))
439 (setq len (- len 4)))
441 (setq result (Xforcenum (aref (X-Dpy-grab-bytes xdpy 1) 0)))
444 (cond ((= mcategory X-XRecordFromServer)
445 ;; Error, Event or Reply
446 (cond ((or (= result 0)
448 ;; Error or Reply .. just flush the data
449 (X-Dpy-grab-bytes xdpy len)
453 (t ;(< result X-MaxEvent)
455 (let ((xev (X-Dpy-parse-event xdpy result)))
456 (setq len (- len 31))
458 ;; Put some interception info
459 (X-Event-put-property xev 'XRecord-Category (nth 1 msg))
460 (X-Event-put-property xev 'XRecord-Sequence (nth 2 msg))
461 (X-Event-put-property xev 'XRecord-Elhead (nth 4 msg))
462 (X-Event-put-property xev 'XRecord-Elhead-value elh-value)
463 (X-Event-put-property xev 'XRecord-Swaped (nth 5 msg))
464 (X-Event-put-property xev 'XRecord-Idbase (nth 6 msg))
465 (X-Event-put-property xev 'XRecord-Servertime (nth 7 msg))
466 (X-Event-put-property xev 'XRecord-RecSeq (nth 8 msg))
468 (X-Dpy-log (X-Event-dpy xev) 'x-record "XRECORD EXTENSION: Get Event: %S(%S[%S]), win=%S"
469 '(X-Event-name xev) '(X-Event-detail xev)
470 '(int-to-char (truncate (car (xwem-kbd-xkcode->xksym (X-Event-detail xev)))))
471 '(X-Win-id (X-Event-win xev)))
473 ;; Add event to event queue
474 (setf (X-Dpy-evq xdpy)
475 (append (X-Dpy-evq xdpy) (list xev)))))))
478 ;; Unsupported category
479 (X-Dpy-grab-bytes xdpy len)
487 ;; To record KeyPress/KeyRelease device events:
489 ;; (setq mrc (make-X-RecordContext :dpy (xwem-dpy) :id (X-Dpy-get-id (xwem-dpy))))
490 ;; (setq mrr (make-X-RecordRange :device-events '(2 . 3)))
491 ;; (setq tcl (X-Win-id (xwem-cl-xwin (xwem-cl-selected))))
492 ;; (setq mrc (X-XRecordCreateContext (xwem-dpy) mrc 5 (list tcl) (list mrr)))
494 ;; (X-XRecordRegisterClients (xwem-dpy) mrc 5 (list tcl) (list mrr))
496 ;; (setq md (XOpenDisplay "127.0.0.1:0"))
497 ;; (setf (X-Dpy-log-buffer md) "XREC.log")
498 ;; (X-XRecordEnableContext md mrc)
501 (provide 'xlib-xrecord)
503 ;;; xlib-xrecord.el ends here