diff options
Diffstat (limited to 'third_party/emacs/exwm/exwm-xim.el')
-rw-r--r-- | third_party/emacs/exwm/exwm-xim.el | 800 |
1 files changed, 0 insertions, 800 deletions
diff --git a/third_party/emacs/exwm/exwm-xim.el b/third_party/emacs/exwm/exwm-xim.el deleted file mode 100644 index acf718e27fd3..000000000000 --- a/third_party/emacs/exwm/exwm-xim.el +++ /dev/null @@ -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 |