mirror of
https://github.com/emacs-exwm/exwm.git
synced 2024-11-23 21:17:59 +01:00
Add input method support
; The code is basically refactored from ; https://github.com/ch11ng/exim to get better maintenance. * exwm-xim.el: New module making Emacs's builtin input methods usable for interacting with X windows. * exwm-core.el (exwm--intern-atom): New function for intern X11 atoms. * exwm-input.el (exwm-input--init): * exwm-manage.el (exwm-manage--init): Use it.
This commit is contained in:
parent
44629818ba
commit
0f7269c4ec
8 changed files with 812 additions and 42 deletions
|
@ -8,7 +8,8 @@ It features:
|
||||||
+ Dynamic workspace support
|
+ Dynamic workspace support
|
||||||
+ ICCCM/EWMH compliance
|
+ ICCCM/EWMH compliance
|
||||||
+ (Optional) RandR (multi-monitor) support
|
+ (Optional) RandR (multi-monitor) support
|
||||||
+ (Optional) Built-in system tray
|
+ (Optional) Builtin system tray
|
||||||
|
+ (Optional) Builtin input method
|
||||||
|
|
||||||
Please check out the
|
Please check out the
|
||||||
[screenshots](https://github.com/ch11ng/exwm/wiki/Screenshots)
|
[screenshots](https://github.com/ch11ng/exwm/wiki/Screenshots)
|
||||||
|
|
|
@ -131,6 +131,15 @@ Nil can be passed as placeholder."
|
||||||
(if height xcb:ConfigWindow:Height 0))
|
(if height xcb:ConfigWindow:Height 0))
|
||||||
:x x :y y :width width :height height)))
|
: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)
|
(defmacro exwm--defer (secs function &rest args)
|
||||||
"Defer the execution of FUNCTION.
|
"Defer the execution of FUNCTION.
|
||||||
|
|
||||||
|
|
|
@ -542,9 +542,9 @@ instead."
|
||||||
(exwm-input--update-global-prefix-keys)))
|
(exwm-input--update-global-prefix-keys)))
|
||||||
|
|
||||||
;; Putting (t . EVENT) into `unread-command-events' does not really work
|
;; Putting (t . EVENT) into `unread-command-events' does not really work
|
||||||
;; as documented for Emacs < 27.
|
;; as documented for Emacs < 26.2.
|
||||||
(eval-and-compile
|
(eval-and-compile
|
||||||
(if (< emacs-major-version 27)
|
(if (string-version-lessp emacs-version "26.2")
|
||||||
(defsubst exwm-input--unread-event (event)
|
(defsubst exwm-input--unread-event (event)
|
||||||
(setq unread-command-events
|
(setq unread-command-events
|
||||||
(append unread-command-events (list event))))
|
(append unread-command-events (list event))))
|
||||||
|
@ -909,7 +909,7 @@ Notes:
|
||||||
* Setting the value directly (rather than customizing it) after EXWM
|
* Setting the value directly (rather than customizing it) after EXWM
|
||||||
finishes initialization has no effect.
|
finishes initialization has no effect.
|
||||||
* Original-keys consist of multiple key events are only supported in Emacs
|
* Original-keys consist of multiple key events are only supported in Emacs
|
||||||
27 and later.
|
26.2 and later.
|
||||||
* A minority of applications do not accept simulated keys by default. It's
|
* A minority of applications do not accept simulated keys by default. It's
|
||||||
required to customize them to accept events sent by SendEvent.
|
required to customize them to accept events sent by SendEvent.
|
||||||
* The predefined examples in the Customize interface are not guaranteed to
|
* The predefined examples in the Customize interface are not guaranteed to
|
||||||
|
@ -1035,14 +1035,7 @@ where both ORIGINAL-KEY and SIMULATED-KEY are key sequences."
|
||||||
(make-instance 'xcb:ewmh:set-_NET_WM_NAME
|
(make-instance 'xcb:ewmh:set-_NET_WM_NAME
|
||||||
:window exwm-input--timestamp-window
|
:window exwm-input--timestamp-window
|
||||||
:data "EXWM: exwm-input--timestamp-window"))
|
:data "EXWM: exwm-input--timestamp-window"))
|
||||||
(let ((atom "_TIME"))
|
(setq exwm-input--timestamp-atom (exwm--intern-atom "_TIME"))
|
||||||
(setq exwm-input--timestamp-atom
|
|
||||||
(slot-value (xcb:+request-unchecked+reply exwm--connection
|
|
||||||
(make-instance 'xcb:InternAtom
|
|
||||||
:only-if-exists 0
|
|
||||||
:name-len (length atom)
|
|
||||||
:name atom))
|
|
||||||
'atom)))
|
|
||||||
;; Initialize global keys.
|
;; Initialize global keys.
|
||||||
(dolist (i exwm-input-global-keys)
|
(dolist (i exwm-input-global-keys)
|
||||||
(exwm-input--set-key (car i) (cdr i)))
|
(exwm-input--set-key (car i) (cdr i)))
|
||||||
|
|
|
@ -715,14 +715,7 @@ border-width: %d; sibling: #x%x; stack-mode: %d"
|
||||||
"Initialize manage module."
|
"Initialize manage module."
|
||||||
;; Intern _MOTIF_WM_HINTS
|
;; Intern _MOTIF_WM_HINTS
|
||||||
(exwm--log)
|
(exwm--log)
|
||||||
(let ((atom-name "_MOTIF_WM_HINTS"))
|
(setq exwm-manage--_MOTIF_WM_HINTS (exwm--intern-atom "_MOTIF_WM_HINTS"))
|
||||||
(setq exwm-manage--_MOTIF_WM_HINTS
|
|
||||||
(slot-value (xcb:+request-unchecked+reply exwm--connection
|
|
||||||
(make-instance 'xcb:InternAtom
|
|
||||||
:only-if-exists 0
|
|
||||||
:name-len (length atom-name)
|
|
||||||
:name atom-name))
|
|
||||||
'atom)))
|
|
||||||
(add-hook 'after-make-frame-functions #'exwm-manage--add-frame)
|
(add-hook 'after-make-frame-functions #'exwm-manage--add-frame)
|
||||||
(add-hook 'delete-frame-functions #'exwm-manage--remove-frame)
|
(add-hook 'delete-frame-functions #'exwm-manage--remove-frame)
|
||||||
(xcb:+event exwm--connection 'xcb:ConfigureRequest
|
(xcb:+event exwm--connection 'xcb:ConfigureRequest
|
||||||
|
|
|
@ -48,7 +48,9 @@
|
||||||
;;; Code:
|
;;; Code:
|
||||||
|
|
||||||
(require 'xcb-randr)
|
(require 'xcb-randr)
|
||||||
|
|
||||||
(require 'exwm-core)
|
(require 'exwm-core)
|
||||||
|
(require 'exwm-workspace)
|
||||||
|
|
||||||
(defgroup exwm-randr nil
|
(defgroup exwm-randr nil
|
||||||
"RandR."
|
"RandR."
|
||||||
|
@ -93,17 +95,8 @@ corresponding monitors whenever the monitors are active.
|
||||||
|
|
||||||
(defvar exwm-randr--last-timestamp 0 "Used for debouncing events.")
|
(defvar exwm-randr--last-timestamp 0 "Used for debouncing events.")
|
||||||
|
|
||||||
(defvar exwm-workspace--fullscreen-frame-count)
|
|
||||||
(defvar exwm-workspace--list)
|
|
||||||
(defvar exwm-randr--prev-screen-change-seqnum nil
|
(defvar exwm-randr--prev-screen-change-seqnum nil
|
||||||
"The most recent ScreenChangeNotify sequence number.")
|
"The most recent ScreenChangeNotify sequence number.")
|
||||||
(declare-function exwm-workspace--count "exwm-workspace.el")
|
|
||||||
(declare-function exwm-workspace--set-active "exwm-workspace.el"
|
|
||||||
(frame active))
|
|
||||||
(declare-function exwm-workspace--set-desktop-geometry "exwm-workspace.el" ())
|
|
||||||
(declare-function exwm-workspace--set-fullscreen "exwm-workspace.el" (frame))
|
|
||||||
(declare-function exwm-workspace--show-minibuffer "exwm-workspace.el" ())
|
|
||||||
(declare-function exwm-workspace--update-workareas "exwm-workspace.el" ())
|
|
||||||
|
|
||||||
(defun exwm-randr--get-monitors ()
|
(defun exwm-randr--get-monitors ()
|
||||||
"Get RandR monitors."
|
"Get RandR monitors."
|
||||||
|
|
|
@ -33,7 +33,9 @@
|
||||||
(require 'xcb-icccm)
|
(require 'xcb-icccm)
|
||||||
(require 'xcb-xembed)
|
(require 'xcb-xembed)
|
||||||
(require 'xcb-systemtray)
|
(require 'xcb-systemtray)
|
||||||
|
|
||||||
(require 'exwm-core)
|
(require 'exwm-core)
|
||||||
|
(require 'exwm-workspace)
|
||||||
|
|
||||||
(defclass exwm-systemtray--icon ()
|
(defclass exwm-systemtray--icon ()
|
||||||
((width :initarg :width)
|
((width :initarg :width)
|
||||||
|
@ -77,12 +79,7 @@ You shall use the default value if using auto-hide minibuffer."
|
||||||
(defvar exwm-systemtray--selection-owner-window nil
|
(defvar exwm-systemtray--selection-owner-window nil
|
||||||
"The selection owner window.")
|
"The selection owner window.")
|
||||||
|
|
||||||
(defvar exwm-workspace--current)
|
|
||||||
(defvar exwm-workspace--minibuffer)
|
|
||||||
(defvar exwm-workspace--workareas)
|
|
||||||
(defvar exwm-workspace-current-index)
|
|
||||||
(defvar xcb:Atom:_NET_SYSTEM_TRAY_S0)
|
(defvar xcb:Atom:_NET_SYSTEM_TRAY_S0)
|
||||||
(declare-function exwm-workspace--minibuffer-own-frame-p "exwm-workspace.el")
|
|
||||||
|
|
||||||
(defun exwm-systemtray--embed (icon)
|
(defun exwm-systemtray--embed (icon)
|
||||||
"Embed an icon."
|
"Embed an icon."
|
||||||
|
|
781
exwm-xim.el
Normal file
781
exwm-xim.el
Normal file
|
@ -0,0 +1,781 @@
|
||||||
|
;;; exwm-xim.el --- XIM Module for EXWM -*- lexical-binding: t -*-
|
||||||
|
|
||||||
|
;; Copyright (C) 2019 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.")
|
||||||
|
(defvar exwm-xim--event-pending nil
|
||||||
|
"Indicating whether Emacs requires more events.")
|
||||||
|
|
||||||
|
;; 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)
|
||||||
|
(let ((im-func (with-current-buffer (window-buffer)
|
||||||
|
input-method-function))
|
||||||
|
key-event keysym event result)
|
||||||
|
;; Note: The flag slot is ignored.
|
||||||
|
;; Do conversion in client's byte-order.
|
||||||
|
(let ((xcb:lsb xim: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)))))))
|
||||||
|
(if exwm-xim--event-pending
|
||||||
|
;; In case any event reaches here, it should be forwarded
|
||||||
|
;; to Emacs.
|
||||||
|
(when event
|
||||||
|
(setq unread-command-events
|
||||||
|
(append unread-command-events (list event))))
|
||||||
|
(setq exwm-xim--event-pending t)
|
||||||
|
(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 (im-id ic-id serial-number event) req
|
||||||
|
(push (make-instance 'xim:forward-event
|
||||||
|
:im-id im-id
|
||||||
|
:ic-id ic-id
|
||||||
|
:flag xim:commit-flag:synchronous
|
||||||
|
:serial-number serial-number
|
||||||
|
:event event)
|
||||||
|
replies))
|
||||||
|
(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))
|
||||||
|
(setq result (funcall im-func event))))
|
||||||
|
(when (eq exwm--selected-input-mode 'char-mode)
|
||||||
|
(exwm-input--release-keyboard)))
|
||||||
|
;; This also works for portable character encoding.
|
||||||
|
(setq result
|
||||||
|
(encode-coding-string (concat result)
|
||||||
|
'compound-text-with-extensions))
|
||||||
|
(message "")
|
||||||
|
(push
|
||||||
|
(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)
|
||||||
|
replies))
|
||||||
|
(setq exwm-xim--event-pending nil))))
|
||||||
|
((= 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--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
|
19
xinitrc
19
xinitrc
|
@ -1,17 +1,20 @@
|
||||||
# Disable access control
|
# Disable access control for the current user.
|
||||||
xhost +SI:localuser:$USER
|
xhost +SI:localuser:$USER
|
||||||
|
|
||||||
# Make Java applications aware this is a non-reparenting window manager.
|
# Make Java applications aware this is a non-reparenting window manager.
|
||||||
export _JAVA_AWT_WM_NONREPARENTING=1
|
export _JAVA_AWT_WM_NONREPARENTING=1
|
||||||
|
|
||||||
# Themes, etc
|
# Set default cursor.
|
||||||
gnome-settings-daemon &
|
|
||||||
|
|
||||||
# Fallback cursor
|
|
||||||
xsetroot -cursor_name left_ptr
|
xsetroot -cursor_name left_ptr
|
||||||
|
|
||||||
# Keyboard repeat rate
|
# Set keyboard repeat rate.
|
||||||
xset r rate 200 60
|
xset r rate 200 60
|
||||||
|
|
||||||
# Start Emacs
|
# Uncomment the following block to use the exwm-xim module.
|
||||||
exec dbus-launch --exit-with-session emacs
|
#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
|
||||||
|
|
Loading…
Reference in a new issue